16_继承

继承image-20231006124450312

派生类的定义

class 子类{};
class 子类:继承方式 父类
{
    //新增子类数据
};

继承方式: private protected public(推荐)

protected关键字: 基类的保护成员只在派生类的成员函数中能访问

image-20231006125521707

所有父类私有在子类中不可访问, 公共继承保持不变, 保护继承变保护, 私有继承变私有

class Base
{
private:
    int a;
protected:
    int b;
public:
    int c;
};
class Son:public Base
{
    //子类中 能访问 Protected b public c
public:
    void func()
    {
        cout << b << c << endl;
        cout << a << endl; //不可访问
    }
};

void main()
{
    Son ob;
    cout << ob.b << endl; //类外无法访问
}

继承中的构造和析构

子类的构造析构顺序

image-20231007155039677

#include <iostream>

using namespace std;

class Base
{
public:
    Base()
    {
        cout << "父类构造" << endl;
    }
    ~Base()
    {
        cout << "父类析构" << endl;
    }
};

class Other
{
public:
    Other()
    {
        cout << "Other构造" << endl;
    }
    ~Other()
    {
        cout << "Other析构" << endl;
    }
};

class Son: public Base
{
public:
    Other ob;
public:
    Son()
    {
        cout << "Son构造" << endl;
    }
    ~Son()
    {
        cout << "Son析构" << endl;
    }
};

int main()
{
    Son ob;
    return 0;
}

子类调用成员对象, 父类的有参构造

子类实例化对象时, 会自动调用成员对象, 父类的默认无参构造

子类实例化对象时必须使用初始化列表 调用成员对象, 父类的有参构造

初始化列表时: 父类写类名称 成员对象用对象名

#include <iostream>

using namespace std;

class Base
{
public:
    int a;
public:
    Base()
    {
        cout << "父类默认构造" << endl;
    }
    Base(int a)
    {
        this->a = a;
        cout << "父类有参构造" << endl;
    }
    ~Base()
    {
        cout << "父类析构" << endl;
    }
};


class Other
{
public:
    int b;
public:
    Other()
    {
        cout << "Other默认构造" << endl;
    }
    Other(int b)
    {
        this->b = b;
        cout << "Other有参构造" << endl;
    }
    ~Other()
    {
        cout << "Other析构" << endl;
    }
};

class Son: public Base
{
public:
    Other ob;
    int c;
public:
    Son()
    {
        cout << "Son默认构造" << endl;
    }
    Son(int a, int b, int c): Base(a), ob(b)
    {
        this->c = c;
        cout << "Son有参构造" << endl;
    }
    ~Son()
    {
        cout << "Son析构" << endl;
    }
};

int main()
{
    Son ob(1, 2, 3);
    return 0;
}

子类和父类的同名处理

同名成员 最简单 最安全的处理方式: 加作用域

子类和父类 同名成员数据

子类默认优先访问 子类的同名成员

必须加父类作用域 访问父类的同名成员

#include <iostream>

using namespace std;

class Base
{
public:
    int a;
public:
    Base(int a)
    {
        this->a = a;
    }
    void fun()
    {
        cout << "Base的fun()" << endl;
    }
};

class Son: public Base
{
public:
    int a;
    Son(int x, int y): Base(x)
    {
        a = y;
    }
    void fun()
    {
        cout << "Son的fun()" << endl;
    }
};

int main()
{
    Son ob(10, 20);
    //同名成员 子类默认优先访问
    cout << ob.a << endl; //20
    //加父类作用域访问父类同名成员
    cout << ob.Base::a << endl; //10
    ob.fun();
    ob.Base::fun();
    return 0;
}

子类 重定义 父类的同名函数

重载: 无继承, 同一作用域, 参数的个数, 顺序, 类型 不同 都可重载

重定义: 有继承, 子类 重定义 父类的同名函数(参数可以不同) 子类一旦重定义父类的同名函数不管参数是否一致, 子类会屏蔽父类所有的同名函数

#include <iostream>

using namespace std;

class Base
{
public:
    void fun()
    {
        cout << "Base的fun()" << endl;
    }
    void fun(int a)
    {
        cout << "Base的fun(int a)" << endl;
    }
    void fun(int a, int b)
    {
        cout << "Base的fun(int a, int b)" << endl;
    }
};

class Son: public Base
{
public:
    void fun(string a)
    {
        cout << "Son的fun(string a)" << endl;
    }
};

int main()
{
    Son ob;
    ob.fun("hello");
    ob.fun(); //error
    ob.fun(5); //error
    ob.fun(10, 20); //error
    return 0;
}

子类不能继承父类的成员

不是所有的函数都能自动从基类继承到派生类中。

父类的构造函数和析构函数用来处理对象的创建和析构操作,父类的构造和析构函数只知道对自己的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每一个特定的派生类分别创建。

另外operator=也不能被继承,因为它完成类似构造函数的行为。也就是说尽管我们知道如何由=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派生类依然有效。在继承的过程中,如果没有创建这些函数,编译器会自动生成它们。

多继承

多继承的格式

class 父类1{};
class 父类2{};
class 子类:继承方式1 父类1, 继承方式2 父类2
{
    //新增子类数据
};
#include <iostream>

using namespace std;

class Base1
{
public:
    int a;
    Base1(int a)
    {
        this->a = a;
    }
};

class Base2
{
public:
    int b;
    Base2(int b)
    {
        this->b = b;
    }
};

class Son: public Base1, public Base2
{
public:
    Son(int a, int b): Base1(a), Base2(b)
    {

    }
    void fun()
    {
        cout << a << ", " << b << endl;
    }
};

int main()
{
    Son ob(1, 2);
    ob.fun();
    return 0;
}

菱形继承

菱形继承: 有公共祖先的继承

最底层的子类数据会包含多份公共祖先数据

image-20231007173609665

class Animal
{
public:
    int data;
};
class Sheep :public Animal{};
class Tuo: public Animal{};
class SheepTuo: public Sheep, public Tuo{};
void main()
{
    SheepTuo ob;
    memset(&ob, 0, sizeof(SheepTuo));
    
    cout << ob.data << endl; //error, 有二义性
    cout << ob.Sheep::data << endl;
    cout << ob.Tuo::data << endl;
}

解决方法: 加作用域

虚继承

虚继承解决菱形继承中多份公共祖先数据的问题

虚继承的方式

在继承方式前加 virtual 修饰, 子类虚继承父类 子类只会保存一份公共数据

image-20231007203301714

虚继承会在子类中产生虚基类指针(vbptr) 指向虚基类表(vbtable),虚基类表纪录的是通过该指针访问公共祖先的数据的偏移量。

注意:虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的

工程开发中真正意义上的多继承是几乎不被使用,因为多重继承带来的代码复杂性远多于其带来的便利,多重继承对代码维护性上的影响是灾难性的,在设计方法上,任何多继承都可以用单继承代替。

posted @ 2023-10-11 16:58  爱吃冰激凌的黄某某  阅读(11)  评论(0编辑  收藏  举报