2605C++,C++类的继承1

原文

C++类的继承

继承是C++``面向对象的三大特征之一,它使一个类可从现有类中继承,而不必从头开始定义.

类继承的实质是以旧类为基础创建新类,新类包含旧类的数据成员和成员函数,且可添加新的数据成员和成员函数.其中,旧类叫做基类或父类,新类叫做继承类或子类.

C++类的继承

继承可得到继承类,其定义形式如下:

cpp 复制代码
class 继承类名 : [继承方式] 基类名
{
    [访问控制修饰符:]
    [成员声明列表]
}

":"符号说明这是一个继承类,":"后为继承的基类;
继承方式有3种,分别为公(公开),保护(保护)和私(私有),默认为是private;
访问控制修饰符也有3种,同样是public,protected和私,默认为是private;
成员声明列表中给出的是继承类中新增的成员变量和成员函数.

如,定义CEmployee员工类,包含3个公开成员变量,员工ID,员工姓名和所属部门:

cpp 复制代码
class CEmployee
{
public:
    int m_ID;
//员工`ID`
    char m_Name[128];
//员工姓名
    char m_Depart[128];
//所属部门
};

再定义一个COperator符号类,使其从CEmployee类继承.此时,COperator类会自动继承CEmployee类的员工ID,员工姓名和所属部门信息,同时可新增员工密码和登录方法.

cpp 复制代码
class COperator : public CEmployee
{
public:
    char m_Password[128];
//新增公开`成员变量``Password`,表示员工密码
    bool Login();
//新增公开`成员函数``Login()`,表示登录方法
};

两个使用继承的典型场景:

待创建的类与当前类类似,只是多出若干成员变量或成员函数时,可用继承;

需要创建多个类,这些类拥有类似的成员变量或成员函数时,可用继承.将共同成员取出来,按基类定义,然后继承快速得到多个类,不但节省代码,还方便后续修改.

实例公开继承员工类,得到符号类,并新增成员信息,实现打卡功能.代码如下:

cpp 复制代码
#include<iostream>
using namespace std;
class CEmployee
{
public:
    int m_ID;
    char m_Name[128];
    char m_Depart[128];
    CEmployee()
    {
        memset(m_Name, 0, 128);
        memset(m_Depart, 0, 128);
    }
    void OutputName()
    {
        cout << "员工姓名:" << m_Name << endl;
    }
};
class COperator : public CEmployee
{
public:
    char m_Password[128];
    bool Login()
    {
        if (strcmp(m_Name, "MR") == 0 && strcmp(m_Password, "KJ") == 0)
        {
            cout << "登录成功!" << endl;
            return true;
        }
        else
        {
            cout << "登录失败!" << endl;
            return false;
        }
    }
};
int main(int argc, char* argv[])
{
    COperator optr;
    strcpy(optr.m_Name, "MR");
    strcpy(optr.m_Password, "KJ");
    optr.Login();
    optr.OutputName();
    return 0;
}

运行程序结果为:

登录成功!

员工姓名:MR

C++继承后的可访问性

公开,私有,保护3种继承方式下,基类中的成员在继承类中的可访问性如下表所示.

表:不同继承方式下基类成员在继承类中的可访问性

继承方式 基类公成员 基类保护成员 基类私成员
公继承方式 public protected 不可见
保护继承方式 protected protected 不可见
私继承方式 private private 不可见

继承方式的几点说明如下:
1),基类成员在继承类中的权限不能高于继承方式指定的权限.如:

1,继承方式为公时,基类成员在继承类中的权限将不变;

2,继承方式为保护时,基类成员在继承类中的权限最高为protected;

3,继承方式为私时,基类成员在继承类中的权限最高为私.

2)基类中的私成员在继承类中不可见,即不能在继承类的成员函数中访问或调用.因此,如果希望继承类可继承且毫无障碍地使用基类成员,可按公或保护属性声明,只有那些不希望在继承类中使用的成员才按私声明.

3)如果希望基类成员既不向外暴露(不能对象访问),还能在继承类中使用,可按保护属性声明它.基类的所有继承类可使用保护成员,这一属性将沿继承树无限向下传播.

注意,在继承类定义时可访问保护类型,不能访问用继承类声明的对象,即类体外不能访问.先来看一下公继承方式的访问示例:

cpp 复制代码
class CEmployee
{
public:
    void Output()
    {
        cout << m_ID << endl;
        cout << m_Name << endl;
        cout << m_Depart << endl;
    }
private:
    int m_ID;
    char m_Name[128];
    char m_Depart[128];
};
class COperator : public CEmployee
{
public:
    void Output()
    {
        cout << m_ID << endl;
//错误,不能引用基类的私有成员`m_ID`
        cout << m_Name << endl;
//错误,不能引用基类的私有成员`m_Name`
        cout << m_Depart << endl;
//错误,不能引用基类的私有成员`m_Depart`
        cout << m_Password << endl;
//`正确`,可引用自己的私有成员`m_Password`
    }
private:
    char m_Password[128];
    bool Login();
};

下面是私继承方式的访问示例:

cpp 复制代码
class CEmployee
{
public:
    void Output()
    {
        cout << m_ID << endl;
        cout << m_Name << endl;
        cout << m_Depart << endl;
    }
public:
    int m_ID;
protected:
    char m_Name[128];
private:
    char m_Depart[128];
};
class COperator : private CEmployee
{
public:
    void Output()
    {
        cout << m_ID << endl;
//`正确`,可引用基类的公开成员`m_ID`
        cout << m_Name << endl;
//`正确`,可引用基类的保护成员`m_Name`
        cout << m_Depart << endl;
//错误,不能引用基类的私有成员`m_Depart`
        cout << m_Password << endl;
//`正确`,可引用自己的私有成员`m_Password`
    }
private:
    char m_Password[128];
    bool Login();
};

私和保护继承方式会更改基类成员在继承类中的权限,导致继承关系复杂,所以实际开发中最常使用的是公继承方式.

C++``构造器的访问顺序

基类和继承类中都有构造器和析构器,则在创建继承类对象时是先构造基类,还是先构造继承类?同样,在释放继承类对象时是先释放基类,还是先释放继承类?

事实上,创建继承类对象时,先调用基类构造器,再调用继承类``构造器;释放继承类对象时则恰好相反,先调用继承类的析构器,再调用基类的析构器.

实例定义基类CEmployee和继承类``COperator,观察构造器和析构器的访问顺序,具体代码如下:

cpp 复制代码
#include<iostream>
using namespace std;
class CEmployee
{
public:
    int m_ID;
    char m_Name[128];
    char m_Depart[128];
    CEmployee()
    {
        cout << "CEmployee类`构造器`被调" << endl;
    }
    ~CEmployee()
    {
        cout << "CEmployee类`析构器`被调" << endl;
    }
};
class COperator : public CEmployee
{
public:
    char m_Password[128];
    COperator()
    {
        strcpy(m_Name, "MR");
        cout << "COperator类`构造器`被调" << endl;
    }
    ~COperator()
    {
        cout << "COperator类`析构器`被调" << endl;
    }
};
int main(int argc, char* argv[])
{
    COperator optr;
    return 0;
}

运行结果为:
CEmployee类构造器被调
COperator类构造器被调
COperator类析构器被调
CEmployee类析构器被调

C++``继承类``显式调用``基类构造器

创建继承类对象时,会自动调用基类默认构造器.若想使用基类带参构造器,则要用显式调用方式.

实例要求继承类``显式调用基类的构造器.具体代码如下:

cpp 复制代码
#include<iostream>
using namespace std;
class CEmployee
{
public:
    int m_ID;
    char m_Name[128];
    char m_Depart[128];
    CEmployee(char name[])
    {
        strcpy(m_Name, name);
        cout << m_Name << "调用了CEmployee类`带参构造器`" << endl;
    }
    CEmployee()
    {
        strcpy(m_Name, "MR");
        cout << m_Name << "CEmployee类`无参构造器`被调" << endl;
    }
    ~CEmployee()
    {
        cout << "CEmployee类`析构器`被调" << endl;
    }
};
class COperator : public CEmployee
{
public:
    char m_Password[128];
    COperator(char name[]) : CEmployee(name)
    {
        cout << "COperator类`构造器`被调" << endl;
    }
    COperator() : CEmployee("JACK")
    {
        cout << "COperator类`构造器`被调" << endl;
    }
    ~COperator()
    {
        cout << "COperator类`析构器`被调" << endl;
    }
};
int main(int argc, char* argv[])
{
    COperator optr1;
    COperator optr2("LaoZhang");
    return 0;
}

运行程序结果为:
JACK调用了CEmployee类带参构造器
COperator类构造器被调
LaoZhang调用了CEmployee类带参构造器
COperator类构造器被调
COperator类析构器被调
CEmployee类析构器被调
COperator类析构器被调
CEmployee类析构器被调

在基类无参构造器中初化成员串数组m_Name为MR.从运行结果上看,继承类``创建对象时没有调用基类无参构造器被调是带参构造器.

C++``继承类屏蔽基类的同名成员函数

如果继承类中定义了一个和基类成员同名的成员函数,则继承类对象调用成员时被调是基类成员函数,还是继承类``成员函数呢?

事实上,基类的同名成员函数(包括重载函数)在继承类中会被屏蔽,因此继承类对象调用是继承类``成员函数.但使用域限定符"::",显式地指定基类名,可调用基类的同名成员函数.

实例基类和继承类中都定义了OutputName()``成员函数,观察继承类对象的调用结果.代码如下:

cpp 复制代码
#include<iostream>
using namespace std;
class CEmployee
{
public:
    int m_ID;
    char m_Name[128];
    char m_Depart[128];
    CEmployee() {}
    ~CEmployee() {}
    void OutputName()
    {
        cout << "调用CEmployee类的OutputName`成员函数`" << endl;
    }
};
class COperator : public CEmployee
{
public:
    char m_Password[128];
    void OutputName()
    {
        cout << "调用COperator类的OutputName`成员函数`" << endl;
    }
};
int main(int argc, char* argv[])
{
    COperator optr;
    optr.OutputName();
//调用`继承类``COperator`的`OutputName()``成员函数`
    return 0;
}

运行程序结果为:

调用COperator类的OutputName``成员函数

可见,代码"optr.OutputName();"调用的是继承类``COperator的OutputName()``成员函数,而不是基类CEmployee的OutputName()``成员函数.

如果用户想访问基类的OutputName()函数,需要在成员函数名前使用域限定符"::",显式地指定基类名.如:

cpp 复制代码
optr.CEmployee::OutputName();
//调用`CEmployee`类的`OutputName()``成员函数`

注意,假设继承子类后,定义了一个基类指针,继承类的构造器为其创建对象.如:

cpp 复制代码
CEmployee *pWorker = new COperator();
//定义`CEmployee`类指针被调`继承类`的`构造器`

使用pWorker对象调用OutputName()函数,如执行代码:

cpp 复制代码
pWorker->OutputName();

则调用是CEmployee类的OutputName()函数.编译器对OutputName()``成员函数是静态绑定,即根据定义对象时的类型,确定调用哪个类的成员函数.
pWorker属于CEmployee类型,因此调用是CEmployee类的OutputName()``成员函数.

相关推荐
wuminyu23 分钟前
ForkJoinPool窃取线程在扫描其他WorkQueue时的随机采样算法与Phase线程挂起唤醒机制解析
java·linux·c语言·jvm·c++
longlongzihan28 分钟前
链表找环:Floyd 判圈算法(LeetCode 141 & 142 )
c++·算法·leetcode·链表
liulilittle40 分钟前
Linux 下 select 测试函数
linux·服务器·网络·数据库·c++·select·c
无忧.芙桃1 小时前
C++语言原理与实践(十二):list类的底层实现
c++·windows·list
沫璃染墨1 小时前
《Linux工程实践篇(二):线程池实战——从线程安全单例到完整源码实现》
linux·开发语言·c++·单例模式·设计模式·系统架构
有只小白叫岳飒2 小时前
c++智能指针 unique_ptr与函数调用
c++
Leo.yuan2 小时前
Kafka+Flink拼凑,还是FineDataLink 5.0一体化?实时数据处理的两种思路
c++·mfc
INGNIGHT3 小时前
944 · 最大子矩阵(前缀和)
数据结构·c++·算法
All for pursuit.4 小时前
【动态规划-6】96.不同的二叉搜索树
数据结构·c++·算法·leetcode·动态规划
大鹏的NLP博客5 小时前
C++ 内存安全的未来:从 Safety Profiles 到 Safe C++
c++·内存安全