类的继承和派生

Posted liberavi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的继承和派生相关的知识,希望对你有一定的参考价值。

类的继承和派生

格式:

class 派生类名:继承方式 基类名, 继承方式 基类名....

{
类体
}

继承形式多种多样:

继承会把基类的所有成员均继承

graph LR
A类-->B
A类-->C
graph LR
A类-->B类
B类-->C类
graph LR
A类-->B类
B类-->C类
A类-->D类
D类-->C类

继承方式有:public, protected, privated

对于继承来的基类是把基类所有成员(除开基类的构造函数与析构函数)都继承了的,仅是对于访问权限的不同.一般说来基类的私有成员,派生类是无法直接访问的,任何继承方式都是这样

  • public:基类的public和protected在派生类中依旧是public和privated,但是基类的privated在派生类中将无法直接访问(可用继承的基类函数对这些私有成员访问进行操作)

  • protected:基类的public和protected在派生类中变成protected,基类的privated在派生类中将无法直接访问

  • privated:基类的public和protected在派生类中变成privated,基类的privated在派生类中将无法直接访问

类的派生:

除开继承来的东西还加入了直接的特性

类的类型兼容

类的类型兼容

#include
using namespace std;

class Base1{ //基类base1定义
public: 
    void display() const{
    cout << "Base1::display()" << endl;
    }
};

class Base2:public Base1{//共有派生类base2定义 
public:
    void display() const{
        cout << "Base2::disply()" << endl; 
    } 
};

class Derived:public Base2{
public:
    void display() const{
        cout << "Derived::display()" << endl;
    }
};

void fun(Base1 *ptr)
{
    ptr->display();
}
int main()
{
    Base1 base1;
    Base2 base2;
    Derived derived;
    fun(&base1);
    fun(&base2);
    fun(&derived);
    return 0;
}

结果:
技术图片


继承类的构造函数、复制函数和析构函数

构造函数(针对1、2UML类型)

  1. 若是都没写构造函数就使用默认构造函数
  2. 若是写了带参数的默认构造函数并且没写默认构造函数,那么在生成派生类的构造函数时需要显示的进行对他们的构造(具体细节我就不写了)
  3. 注意事项:在默认构造函数的初始化列表里,可以对类的对象、基类的默认构造函数、派生类的数据成员进行初始化,但不能进行派生类的委托构造函数,委托构造函数要单独进行
类的构造函数

#include
using namespace std;

class Base1 {
public:
    Base1(int i) { cout << " Constructing Base1" << endl; }
    Base1() = default;
};

class Base2 {
public:
    Base2(int j) { cout << "Constructing Base2" << endl; }
    Base2() = default;
};

class Base3 {
public:
    Base3() { cout << "Constructing Base3 *" << endl; }
};

class Derived :public Base2, public Base1, public Base3 {
public:
    Derived(int a, int b, int c, int d) :member2(d), member1(c), Base1(a), Base2(b){}
    Derived(int k) :Derived() { w = k, h = k; }
    Derived() = default;
private:
    Base1 member1;
    Base2 member2;
    Base3 memeber3;
    int w, h;
};

int main()
{
    Derived obj(1, 2, 3, 4);
    return 0;
}

技术图片

先按照继承顺序对类进行构造初始化,再按照对象顺序对对象进行初始化,图中第24行的委托构造函数要单独进行

复制函数

  • 从基类继承来的成员,其复制构造函数也要通过基类的复制构造函数来实现
  • 派生类的复制构造函数要负责给基类的复制构造函数传递参数
  • 派生类的复制构造函数只能接受一个参数,此参数不仅用来初始化派生类定义的成员,也将被传递给基类的复制构造函数
  • 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用

析构函数

  • 析构函数不被继承,派生类如果需要要自行声明析构函数
  • 声明方法与一般(无继承关系时)类的析构函数相同
  • 析构函数的调用次序与构造函数相反
    • 首先析构对象成员,其析构次序与构造次序相反
    • 然后执行基类的析构函数,执行次序与构造函数的执行次序相反
    • 首先执行派生类析构函数
派生类析构

#include
using namespace std;
class Base1 {
public:
    Base1(int i)
    {
        cout << "Constructing Base1" << i << endl;
    }
    ~Base1() { cout << "Destructing Base1" << endl; }
};

class Base2 {
public:
    Base2(int j)
    {
        cout << "Constructing Base2" << j << endl;
    }
    ~Base2() { cout << "Destructing Base2 " << endl; }
};

class Base3 {
public:
    Base3() { cout << "Constructing Base3 *" << endl; }
    ~Base3() { cout << "Destructing Base3" << endl; }
};

class Derived :public Base2, public Base1, public Base3 {
public:
    Derived(int a, int b,int c, int d):Base1(a), member2(d), member1(c), Base2(b){}
private:
    Base1 member1;
    Base2 member2;
    Base3 member3;
};

int main()
{
    Derived obj(1, 2, 3, 4);
    return 0;
}

作用域

  • 派生类与基类的同名成员(参数个数类型也相同)会对其进行覆盖
  • 派生类的基类们有相同的成员函数名,派生类不能直接去调用,会产生二义性
    • 用类名限定c1.A::f() 或 c1.B::f()
    • 同名隐藏,再在派生类中的同名成员函数中调用A::f()或者B::f()

技术图片

虚基类

解决问题:当派生类从多个基类派生,而这些基类又有共同基类,则在访问此共同基类中的成员时,将产生冗余,并有可能因冗余带来不一致性

虚基类的声明:

  • 以virtual说明基类继承方式
  • class B1:virtual public B

虚基类的作用:

  • 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题
  • 为最远的派生类提供唯一的基类成员,而不是重复产生多次

注意

在第一级继承时就要将共同基类设计为虚基类

虚基类继承

#include
using namespace std;

class Base0 {
public:
    int var0;
    void fun0() { cout << "Member of Base0" << endl; }
};

class Base1 :virtual public Base0 {
public:
    int var1;
};

class Base2 :virtual public Base0 {
public:
    int var2;
};

class Derived :public Base1, public Base2 {
public:
    int var;
    void fun() {
        cout << "Member of Derived" << endl;
    }
};

int main()
{
    Derived d;
    d.var0 = 2;
    d.fun0();
    cout << &d.Base1::var0 << endl;
    cout << &d.Base2::var0 << endl;
    return 0;
}

虚基类的构造函数

  • 建立对象时所指定的类称为最远派生类
  • 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的
  • 在整个继承结构中,直接或者间接继承虚基类的所有派生类,都必须在构造函数的成员初始化列表中为虚基类的构造函数列出参数。如果未列出,则代表调用该虚基类的默认构造函数
  • 在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,其他类对虚基类类构造函数的调用被忽略
虚基类的构造

#include
using namespace std;

class Base0 {
public:
    Base0(int var):var0(var){}
    int var0;
    void fun0() { cout << "Member of Base0" << endl; }
};
class Base1 :virtual public Base0 {
public:
    Base1(int var) :Base0(var) {}
    int var1;
};

class Base2 :virtual public Base0 {
public:
    Base2(int var) :Base0(var){}
    int var2;
};

class Derived :public Base1, public Base2 {
public:
    Derived(int var) :Base0(var), Base1(var), Base2(var){}
    int var;
    void fun()
    {
        cout << "Member of Derived" << endl;
    }
};

int main()
{
    Derived d(1);
    d.var0 = 2;
    d.fun0();
    return 0;
}

以上是关于类的继承和派生的主要内容,如果未能解决你的问题,请参考以下文章

类的继承与派生

继承知识总结

面向对象的继承和派生

Day 5-2 类的继承和派生,重用

继承与派生

Part7 继承与派生 7.1继承的基本概念和语法 7.2 继承方式