• C++ 继承和派生


    继承和派生

    一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。
    从父类产生一个子类,称为派生。
    基类与派生类的关系:派生类是基类的具体化,而基类是派生类的抽象。

    定义基类和派生类

    定义基类

    例:一个矩形类。

    /*矩形类*/
    class Rectangle
    {
    protected:/*被保护成员*/
    	double L = 0;
    	double W = 0;
    public:   /*共有成员*/
    	Rectangle() = default;
    	Rectangle(double a, double b) : L{ a }, W{ b } {}
    	double GetArea() const	/*矩形面积*/
    	{
    		return L * W;
    	}
    	double GetGirth() const	/*矩形周长*/
    	{
    		return  2 * (L + W);
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    基类成员的访问属性
    • 共有成员(public member)可以在程序的任何地方被访问。
    • 私有成员(private member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用。
    • 被保护成员(protected member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用,但可以被派生类的成员函数使用。

    如果省略成员访问限定符,则系统默成员都是私有的。
    public、private、protected 关键字被称为成员访问限定符。

    定义派生类

    派生类必须通过使用类派生列表(class derivation list)明确指出它是从哪个(或哪些)基类继承而来的。
    类派生列表的形式是:

    class  派生类名:[继承方式]  基类名
    {
        派生类新增加的成员
    };
    
    • 1
    • 2
    • 3
    • 4

    首先是一个冒号,后面紧跟以逗号分隔的基类列表。
    其中每个基类前面可以有以下三种访问说明符中的一个:public、protected 或者 private。

    派生类的构造函数

    派生类可以接收基类全部的成员,但是不能继承构造函数和析构函数。
    派生类必须使用基类的构造函数来初始化它的基类部分。
    例:

    /*长方体类*/
    class Cuboid : public Rectangle
    {
    private:
    	double H = 0;
    public:
    	Cuboid() = default;
    	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
    	// Rectangle{ a,b }是基类的构造函数
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    执行派生类构造函数的顺序:
    先调用基类构造函数,对基类数据成员初始化;
    然后执行派生类构造函数本身,对派生类数据成员初始化。

    派生类的拷贝构造和移动构造函数

    在默认情况下,基类默认构造函数初始化派生类对象的基类部分。
    如果我们想拷贝((或移动)基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝(或移动)构造函数。

    合成的拷贝、赋值、移动操作

    某些定义基类的方式也可能导致有的派生类成员成为被删除的函数:

    • 如果基类中的默认构造函数、拷贝构造函数、拷贝赋值运算符或析构函数是被删除的函数或者不可访问,则派生类中对应的成员将是被删除的。
    • 如果在基类中有一个不可访问或删除掉的析构函数,则派生类中合成的默认和拷贝构造函数将是被删除的。
    • 如果基类的移动操作是删除的或不可访问的,则派生类的移动操作也将是被删除的。
    • 如果基类的析构函数是删除的或不可访问的,则派生类的移动构造函数也将是被删除的。
    继承的构造函数

    C++11标准允许派生类继承基类构造函数的方式是提供一条注明了(直接)基类名的 using 声明语句。

    /*正方形类*/
    class Square
    {
    protected:
    	double L;
    public:
    	Square() :L{ 4 } {};
    	Square(double a) :L{ a } {}
    };
    /*立方体类*/
    class Cube : public Square
    {
    public:
    	Square::Square; 	
    /*
    等价于下列构造函数:
    Cube() :Square() {};
    Cube(double a) :Square{ a } {}
    */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    一个构造函数的 using 声明不会改变该构造函数的访问级别。
    一个 using 声明语句不能指定 explicit 或 constexpr。

    继承方式

    通过指定继承方式,可以改变基类成员在派生类中的访问属性。
    公用继承
    公用继承(public inheritance)基类的共有成员和被保护成员在派生类中保持原有访问属性。
    派生类可以访问基类的共有成员和被保护的成员。

    class Cuboid : public Rectangle
    {
    private:
    	double H = 0;
    public:
    	Cuboid() = default;
    	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
    	double GetVolume() const           /*长方体体积*/
    	{//访问基类被保护成员L、W
    		double V = L * W * H;  //底面积乘高
    		return V;
    	}
    	double GetSurfaceArea() const   /*长方体表面积*/
    	{//访问基类共有成员GetArea(),GetGirth()
    		double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积
    		return S;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    派生类的对象可以访问基类的共有成员和本类的共有成员。

    	Cuboid Cu{ 3,3,3 };
    	cout << "底面积:" << Cu.GetArea() << endl;
    	cout << "体积:" << Cu.GetVolume() << endl;
    	cout << "底周长:" << Cu.GetGirth() << endl;
    	cout << "表面积:" << Cu.GetSurfaceArea() << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    私有继承和受保护的继承
    受保护的继承(protected inheritance)基类的共有成员和被保护成员在派生类中成了被保护成员。
    私有继承(private inheritance)基类的共有成员和被保护成员在派生类中成了私有成员。
    派生类可以访问基类的共有成员和被保护的成员。

    class Cuboid : protected Rectangle
    {
    private:
    	double H = 0;
    public:
    	Cuboid() = default;
    	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
    	double GetVolume() const   /*长方体体积*/
    	{//访问基类被保护成员L、W
    		double V = L * W * H;  //底面积乘高
    		return V;
    	}
    	double GetSurfaceArea() const   /*长方体表面积*/
    	{//访问基类共有成员GetArea(),GetGirth()
    		double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积
    		return S;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果基类的共有成员在派生类中成了被保护或私有成员,则派生类的对象只可以访问本类的共有成员。

    	Cuboid Cu{ 3,3,3 };
    	cout << "体积:" << Cu.GetVolume() << endl;
    	cout << "表面积:" << Cu.GetSurfaceArea() << endl;
    
    • 1
    • 2
    • 3
    改变个别成员的可访问性

    有时我们需要改变派生类继承的某个名字的访问级别,通过使用 using 声明可以达到这一目的:

    /*长方体类*/
    class Cuboid : public Rectangle
    {
    private:
    	double H = 0;
    public:
    	using Rectangle::W; //受保护的成员变成共有成员
    	Cuboid() = default;
    	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    派生类到基类的隐式类型转换

    1. 派生类对象能向基类对象赋值。(派生类增加的部分会被切掉。)
    2. 派生类对象能向基类对象的引用进行赋值或初始化。
    3. 派生类对象的地址能赋给指向基类对象的指针变量。
    4. 如果函数参数是基类的对象或基类对象的引用,相应的实参能用派生类对象。

    通过基类的对象、基类对象的引用或指向基类的指针,只能访问派生类中基类部分的共有成员,而不能访问派生类增加的成员。
    不存在基类向派生类的隐式类型转换。

    防止继承的发生

    有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑它是否适合作为一个基类。C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字 final。

    /*矩形类*/
    class Rectangle final  //不允许被继承
    {
    protected:/*被保护成员*/
    	double L = 0;
    	double W = 0;
    public:   /*共有成员*/
    	Rectangle() = default;
    	Rectangle(double a, double b) : L{ a }, W{ b } {}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    python读取Excel指定范围并转为数组
    php面向对象-抽象一个类
    【基本算法题-2022.7.26】3.最短Hamilton路径
    网络基础学习
    JS_——九九乘法表
    stm32工程中的DebugConfig、Listings和Objects 3个文件夹
    【C++入门篇】深入理解函数重载
    【23种设计模式】装饰模式(九)
    如何将项目SpringBoot部署在Linux上?Linux项目部署基本知识。
    JAVA后端开发面试基础知识(四)——计算机网络
  • 原文地址:https://blog.csdn.net/w541541/article/details/134483637