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_NameMR.从运行结果上看,继承类``创建对象时没有调用基类无参构造器被调是带参构造器.

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();"调用的是继承类``COperatorOutputName()``成员函数,而不是基类CEmployeeOutputName()``成员函数.

如果用户想访问基类的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()``成员函数.

相关推荐
王老师青少年编程1 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串排序】:[NOIP 1998 提高组] 拼数
c++·字符串·csp·高频考点·信奥赛·拼数·字符串排序
草莓熊Lotso1 小时前
【Linux网络】从 0 到 1 实现高性能 UDP 聊天室:深入拆解 Linux 网络编程与线程池架构
linux·运维·服务器·网络·数据库·c++·udp
basketball6161 小时前
C++ iomanip 常用函数
开发语言·c++
sanqima1 小时前
C++里strcpy()拷贝的3种写法
c++·字符串拷贝
艾莉丝努力练剑1 小时前
【Linux网络】Linux 网络编程:应用层自定义协议与序列化(2)序列化与反序列化
linux·运维·服务器·c++·网络协议·序列化
智者知已应修善业1 小时前
【51单片机一个按键切合初始流水灯按一下对半闪烁按一下显示时间】2023-10-16
c++·经验分享·笔记·算法·51单片机
雪度娃娃2 小时前
结构型设计模式——外观模式
c++·设计模式·外观模式
蜡笔小马2 小时前
05.C++设计模式-适配器模式
c++·设计模式·适配器模式
code_pgf2 小时前
Python `asyncio` 与 C++ Fiber 的原理与逻辑分析
c++·人工智能·python