派生与继承

单继承

继承:继承是面向对象程序设计中最重要的机制,这种机制提供了无限重复,利用程序资源的一种途径,c++中的继承机制,可以扩充和完善旧的程序设计以适应新的需求,节省程序开发时间和资源。

#include <iostream>
using namespace std;
class Student{
private:
    int num;
    char name[20];
    char sex;
    int age;
    char addr[20];

public:
    void display() {
        cout << "num:" << num << endl;  // 此行已存在
        cout << "name:" << name << endl;// 此行已存在
        cout << "sex:" << sex << endl;// 此行已存在
        cout << "age:" << age << endl;// 此行已存在
        cout << "addr:" << addr << endl;// 此行已存在
    }
};
int main() {
    return 0;
}

利用原来定义的类Student作为基础,再加上新的内容即可,以减少重复的工作量。C++提供的继承机制就是为了解决此问题,在c++中的继承就是在一个已经存在的类基础上建立一个新的类,已存在的类成为“基类(base class)”"父类(father class)"。新建立的类成为"派生类(derived class)"或者"子类(son class)"

#include <iostream>
using namespace std;
class Student {
private:
    int num;
    char name[20];
    char sex;
    int age;
    char addr[20];

public:
    void display() {
        cout << "num:" << num << endl;  // 此行已存在
        cout << "name:" << name << endl;// 此行已存在
        cout << "sex:" << sex << endl;// 此行已存在
        cout << "age:" << age << endl;// 此行已存在
        cout << "addr:" << addr << endl;// 此行已存在
    }
};

class Student1 :public Student {
private:
    int age; // 新增的数据成员
    string addr; // 新增的数据成员
public:
    void display_1() { // 新增加的成员函数
        cout << "age:" << age << endl;
        cout << "addr:" << addr << endl;
    }
};
int main() {
    return 0;
}

在c++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生,从一个基类派生的继承称为单继承,从多个基类派生称为多继承。
c++派生与继承
        通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。称已存在的用来派生新类的类为基础,又称为父类。由已经存在的类派生出的新类称为派生类,又称为子类。
        在建立派生类的过程中,基类不会做任何改变,派生类则除了继承基类的所有可引用的成员变量和成员函数外,还可以另外定义本身的成员变量和处理这些变量的函数,由于派生类可继承基类的成员变量和成员函数,因此在基类中定义好的数据和函数等的程序代码可以重复使用,提高程序的可靠性。
        当从已有的类中派生出新的类时,可以对派生类做出如下几种改变:

  • 可以继承基类的成员数据活成员函数。
  • 可以增加新的成员变量。
  • 可以增加新的成员函数。
  • 可以重新定义已有的成员函数
  • 可以改变现有的成员属性。

在c++中有两种继承,单一继承和多重继承,当一个派生类仅由一个基类派生时,称为单一继承,当一个派生类由两个或多个基类所派生时,成为多重继承。
c++派生与继承
但派生并不是简单的扩充,有可能改变基类的性质。有三种派生方式:“共有派生”,“保护派生”,“私有派生(默认)”
c++派生与继承

公有派生public

class ClassName:public BaseClassName

公有派生时,基类中所有成员在派生类中保持各个成员的访问权限。公有派生,派生类中保持基类成员特性。
基类:

public:在派生类和类外可以使用。
protected:在派生类中使用。
private:不能在派生类中使用。

c++派生与继承
如上图,x在类B新增加的成员中不能直接调用,y在类B中可以调用,z在整个文件中可以调用。对于类B的对象初始化即是对x,y,z,m,n等全部成员的初始化。

#include <iostream>
using namespace std;

// 基类
class A {
public:
    int z;
    A(int a, int b, int c) {
        cout << "调用类A带参的构造函数:" << endl;
        x = a;
        y = b;
        z = c;    
    }
    int getx() {
        return x;
    }
    int gety() {
        return y;
    }
    int getz() {
        return z;
    }
    void print() {
        cout << "\nx=" << x << ",y=" << y << ",z=" << z << endl;
    }
protected:
    int y;
private:
    int x;
};

class B :public A {
public:
    B(int a, int b, int c, int d, int e) :A(a, b, c) {
        cout << "调用派生类B带参的构造函数:" << endl;
        m = d;
        n = e;
    };
    void print() {
        cout << "\nx=" << getx() << ",y=" << y << ",z=" << z << ",m=" <<m <<",n=" <<n <<endl;
    }
    int sum() {
        return (getx() + y + z + m + n);
    }
private:
    int m, n;
};
int main() {
  //  A obj(1, 2, 3);
   // obj.print();

    B obj2(1, 2, 3, 4, 5); // 派生类创建对象obj2,并且初始化参数分别为1,2,3,4,5
    obj2.print();
    cout << "sum" << obj2.sum() << endl;
    cout << "x=" << obj2.getx() << endl;
    cout << "y=" << obj2.gety() << endl;
    cout << "z=" << obj2.getz() << endl;
    return 0;
}

c++派生与继承

私有派生private

class ClassName:private BaseClassName

私有派生时,基类中公有成员和保护成员在派生类中均变为私有的,在派生类中仍可直接使用这些成员,基类中的私有成员,在派生类中不可直接使用。
基类

public:(变为私有)在派生类中使用,类外不可使用。
protected:(变为私有)在派生类中使用,类外不能用。
private:不能在派生类中和类外使用。

c++派生与继承

  • x在类B新增加的成员中不能直接调用
  • y在类B中可以调用
  • z在类B中可以调用

对类B的对象初始化即是对x,y,z,m,n等全部成员的初始化。

#include <iostream>
using namespace std;

// 基类
class A {
public:
    int z;
    A(int a, int b, int c) {
        cout << "调用类A带参的构造函数:" << endl;
        x = a;
        y = b;
        z = c;    
    }
    int getx() {
        return x;
    }
    int gety() {
        return y;
    }
    int getz() {
        return z;
    }
    void print() {
        cout << "\nx=" << x << ",y=" << y << ",z=" << z << endl;
    }
protected:
    int y;
private:
    int x;
};

class B :private A {
public:
    B(int a, int b, int c, int d, int e) :A(a, b, c) {
        cout << "调用派生类B带参的构造函数:" << endl;
        m = d;
        n = e;
    };
    void print() {
        cout << "\nx=" << getx() << ",y=" << y << ",z=" << z << ",m=" <<m <<",n=" <<n <<endl;
    }
    int sum() {
        return (getx() + y + z + m + n);
    }
private:
    int m, n;
};
int main() {

    B obj2(1, 2, 3, 4, 5); // 派生类创建对象obj2,并且初始化参数分别为1,2,3,4,5
    obj2.print();
    cout << "sum" << obj2.sum() << endl;
    //cout << "x=" << obj2.getx() << endl; // 报错
    //cout << "y=" << obj2.gety() << endl;  // 报错
    //cout << "z=" << obj2.getz() << endl;  // 报错
    return 0;
}

c++派生与继承

保护派生类protected

class ClassName:protected BaseClassName

保护派生,派生类中基类公有和保护成员降级使用。保护派生时,基类中公有成员和保护乘员在派生类中均变为保护的和私有的,在派生类中仍可直接使用这些成员,基类中的私有成员在派生类中不可直接使用。
基类:

public:(变为保护)在派生类中使用,类外不可使用。
protected:(变为私有)在派生类中使用,类外不可使用。
private:不能在派生类中和类外使用。

c++派生与继承
x在类B``新增加的成员中不能直接调用,y在类B中可以调用,z在类B中可以直接调用,对类B的对象初始化即是对x,y,z,m,n等全部成员的初始化。
protected 成员是一种具有血缘关系内外有别的成员。它对派生类的对象而言,是公开成员,可以访问,对血缘外部而言,与私有成员一样被隐蔽。

抽象类与保护的成员函数

当定义了一个类,这个类只能用作基类来派生出新的类,而不能用这种类来定义对象时,称这种类为抽象类。当对某些特殊的对象要进行很好地封装时,需要定义抽象类。
将类的构造函数或析构函数的访问权限定义为保护的时,这种类为抽象类。
当把类中的构造函数或析构函数说明为私有的时,所定义的类通常是没有任何实用意义的,一般情况下,不能用它来产生对象,也不能用它来产生派生类。

c++派生与继承

x在类B新增加的成员中不能直接调用
y在类B中可以调用
z在类B中可以调用

对类B的对象初始化即是对x,y,z,m,n等全部成员的初始化。

#include <iostream>
using namespace std;

// 基类
class A {
public:
    int x, y;
    void print() {
        cout << "x=" << x << ",y=" << y << endl;
    }
    A() {
        cout << "调用类A的默认构造函数" << endl;
    }
    A(int a, int b) {
        cout << "调用类A的带参构造函数" << endl;
        x = a;
        y = b;
    }

protected:
};

class B :public A {
public:
    // 可以在派生类中调用A的构造函数
    B(int a, int b, int c) :A(a, b)
    {
        cout << "调用类B:带参构造函数" << endl;
        m = c;
    }
    void print() {
        cout << "x=" << x << ",y=" << y << ",m=" << m << endl;
    }
private:
    int m;
    A obj; // 在派生类中也不可以定义A的对象,实际上还是类外调用
};
int main() {
    B obj(1,2,3);
    obj.print();
    return 0;
}

c++派生与继承

多继承

可以用多个基类来派生一个类。
格式:

class 类名 :<Access>类名1, 类名2, ..., <Access>类名n{
    private:...// 私有
    public:...//公有
    protected:... // 保护
}
class D :public A, protected B, private C {
    ...//派生类中新增成员
};

c++派生与继承

#include <iostream>
using namespace std;

// 基类
class A {
public:
    A(int a) {
        cout << "调用基类A的构造函数." << endl;
        x = a;
    }
    ~A() {
        cout << "调用基类A析构函数." << endl;
    }
private:
    int x;
};

class B {
public:
    B(int a) {
        cout << "调用基类B的构造函数." << endl;
        y = a;
    }
    ~B() {
        cout << "调用基类B析构函数." << endl;
    }
private:
    int y;
};

class C :public A, public B {
public:
    C(int a,int b):A(a),B(100) {
        cout << "调用派生类C构造函数" << endl;
        z = a;
    }
    ~C(){
        cout << "调用派生类C析构函数" << endl;
    }
private:
    int z;
};
int main() {
    C obj(10, 50);
    return 0;
}

c++派生与继承
初始化基类成员
构造函数不能被继承,派生类的构造函数必须调用基类的构造函数来初始化基类成员基类子对象。派生类构造函数的调用顺序如下:

基类的构造函数
子对象类的构造函数
派生类的构造函数

#include <iostream>
using namespace std;

// 基类
class A {
public:
    A() {
        cout << "调用基类A的默认构造函数." << endl;
    }
    A(int a) {
        cout << "调用基类A的构造函数." << endl;
        x = a;
    }
    ~A() {
        cout << "调用基类A析构函数." << endl;
    }
private:
    int x;
};

class B {
public:
    B(int a) {
        cout << "调用基类B的构造函数." << endl;
        y = a;
    }
    ~B() {
        cout << "调用基类B析构函数." << endl;
    }
private:
    int y;
    A obj1;
};

class C :public A, public B {
public:
    C(int a,int b):A(a),B(100) {
        cout << "调用派生类C构造函数" << endl;
        z = a;
    }
    ~C(){
        cout << "调用派生类C析构函数" << endl;
    }
private:
    int z;
};


int main() {
    C obj(10, 20);
    return 0;
}

c++派生与继承

虚继承及其他特性

冲突

多继承出现的冲突,如下
c++派生与继承

#include <iostream>
using namespace std;

// 基类
class A {
public:
    int x;
    A(int a = 0) {
        x = a;
    }
    void print(){
        cout << "调用类A的print()函数" << endl;
        cout << "x=" << x << endl;
    }
private:
};
class B {
public:
    int x;
    B(int a = 0) {
        x = a;
    }
    void print() {
        cout << "调用类B的print()函数" << endl;
        cout << "x=" << x << endl;
    }
private:
};

class C {
public:
    int x;
   C(int a = 0) {
        x = a;
    }
    void print() {
        cout << "调用类C的print()函数"<< endl;
        cout << "x=" << x << endl;
    }
private:
};

class D:public A,public B, public C{
public:
    int d;
    D(int a = 0) {
        d = a;
    }
public:
    void setx(int a) {
        A::x = a;
    }
    void setd(int b) {
        d = b;
    }
    int getd() {
        return d;
    }
private:
};


int main() {
    D d1;
    d1.setx(100);
    d1.A::print();
    return 0;
}

c++派生与继承

支配原则

c++派生与继承

#include <iostream>
using namespace std;

// 基类A
class A {
public:
    int x;
    void print(){
        cout << "调用类A的print()函数" << endl;
        cout << "x=" << x << endl;
    }
private:
};
// 基类B
class B {
public:
    int y;
    void print() {
        cout << "调用类B的print()函数" << endl;
        cout << "y=" << y << endl;
    }
private:
};

class C:public A,public B {
public:
    int y;
private:
};

int main() {
    C c1;
    c1.x = 100;
    c1.y = 200; // 未知名的情况下,给派生类中的y赋值
    c1.B::y = 300; // 给基类B的y赋值
    c1.A::print();
    c1.B::print();
    cout << "--------------------" << endl;
    cout << "y=" << c1.y << endl; // 输出派生类的y的值
    cout << "y=" << c1.B::y << endl; // 输出基类B中y的值
    return 0;
}

c++派生与继承

基类与对象成员

任一基类在派生类中只能继承一次,否则会照成成员名冲突;
若在派生类中,确实要有两个以上的基类成员,则可用基类的两个对象作为派生类的成员,把一个类作为派生类的基类或把一个类的对象作为一个类的成员,在使用上区别,在派生类中可以直接使用基类的成员(访问权限允许的话),但要使用对象成员的成员时,必须在对象名后加上成员运算符"."和成员名。

赋值兼容规则

c++派生与继承
c++派生与继承
c++派生与继承

虚基类

c++派生与继承
c++派生与继承

在 C++ 中,‌虚基类(Virtual Base Class)‌的主要作用是‌解决多重继承中的“菱形继承”问题(Diamond Problem)‌。
具体来说,它解决了以下两个核心痛点:
‌- 消除数据冗余‌:确保公共基类在最终派生类中只存在‌一份‌实例,而不是多份拷贝。
‌- 消除访问二义性‌:避免因为存在多个基类副本而导致编译器无法确定访问哪一个成员。

假设有一个类层次结构如下:

基类 A
类 B 继承自 A
类 C 继承自 A
类 D 同时继承自 B 和 C

如果不使用虚基类(普通继承):

  • B 中包含一份 A 的成员。
  • C 中也包含一份 A 的成员。
  • 当 D 继承 B 和 C 时,D 的对象中会包含‌两份‌ A 的副本(一份来自 B,一份来自 C)。

这会带来两个严重问题:‌

  • 内存浪费‌:如果 A 中有大数据成员,D 中就会存储两份相同的数据。
  • 二义性错误‌:如果你尝试在 D 中访问 A 的成员(例如 d.x),编译器会报错,因为它不知道你是想访问“来自 B 的那份 A”还是“来自 C 的那份 A”。你必须写成 d.B::x 或 d.C::x 才能通过编译,这非常繁琐且容易出错。

如果使用虚基类:
在 B 和 C 继承 A 时,加上 virtual 关键字:

class B : virtual public A { ... };
class C : virtual public A { ... };
class D : public B, public C { ... };

‌效果:‌
无论经过多少条路径继承,A 在 D 的对象中‌只保留一份实例‌。B 和 C 共享这唯一的一份 A。在 D 中访问 A 的成员时,直接写 d.x 即可,没有二义性。

#include <iostream>
using namespace std;

// 基类A
class A {
public:
        int x;
    A(int a = 0) {
        cout << "调用类A的构造函数." << endl;
        x = a;
    }
};
// 基类B
class B:virtual public A {
public:
    int y;
    B(int a = 0,int b=0):A(a) {
        cout << "调用类B的构造函数." << endl;
        y = a;
    }
};

class C:virtual public A {
public:
    int z;
    C(int a = 0, int c = 0) :A(a) {
        cout << "调用类C的构造函数." << endl;
        z = c;
    }
private:
};

class D : public B, public C {
public:
    int dx;
    D(int a1, int b, int c, int d, int a2) :B(a1, b), C(a2, c) {
        cout << "调用类D的构造函数." << endl;
        dx = d;
    }
};

int main() {
    D obj(100, 200, 300, 400, 500);
    obj.x = 2000;
    cout << "obj.x" << obj.x << endl;
    return 0;
}

c++派生与继承

更多推荐