继承的基本概念

例如猫和狗都可以有年龄,姓名的属性。那么就可以用一个父类animal动物。给父类年龄和姓名。子类就会继承这些属性。可以大大减少代码量。比如现在有需要十种不同的动物类,就可以把他们的共性抽象为一个父类即可。

#include<iostream>
using namespace std;
#include<string>
class animal {
public:
    int age = 18;
    string name = "abc";
};
class Dog :public animal {
public:
    int height = 50;
};
void test01()
{
    Dog dog;
    cout << dog.name << endl;
    cout << dog.age << endl;
    cout << dog.height << endl;
}
int main()
{
    test01();
}

总结:

  • 好处:可以减少重复的代码
  • 语法:class A: public B;
  • A类称为子类或派生类,B类称为父类或基类。

派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。

继承方式

继承方式一共有三种:·公共继承,保护继承,私有继承
2025-11-05T08:56:53.png

#include<iostream>
using namespace std;
#include<string>
class Base1
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;

};
class Son1 :public Base1 {
public:
    void func()
    {
        m_A = 10;//父类公共权限成员,在子类依然是公共
        m_B = 10;//父类保护权限成员,在子类依然是保护权限
        //m_C = 10;报错,父类的私有成员,子类访问不到
    }
};
class Son2 :protected Base1 {
public:
    void func() {
        m_A = 100;//父类公共权限成员,在子类变为保护权限
        m_B = 100;//父类保护权限成员,在子类依然是保护权限
        //m_C = 100;报错,父类的私有成员,子类访问不到
    }
};
class Son3 :private Base1 {
public:
    void func()
    {
        m_A = 100;//父类公共权限成员,在子类变为私有权限
        m_B = 100;//父类保护权限成员,在子类变为私有权限
        //m_C = 100;报错,父类的私有成员,子类访问不到
    }
};
class GrandSon3 :public Son3
{
public:
    void func()
    {
        //m_A = 10;报错,父类Son3的m_A已经变为了私有,在Son3的子类就无法访问。
        //侧面验证了,private权限继承之后的属性确实变为了私有的。
    }
};
int main()
{
    Son1 s1;
    s1.m_A = 10;
    cout << s1.m_A << endl;
    //s1.m_B = 10;报错,父类的保护权限在子类依然是保护权限,不能在类外访问。
    Son2 s2;
    //s2.m_A = 10;报错,m_A已经在子类变为了保护权限,类外访问不到。
    Son3 s3;
    //s3.m_A = 10;报错,m_A已经在子类变为了私有权限,类外访问不到。
}

继承中的对象模型

#include<iostream>
using namespace std;
#include<string>
class Base {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;

};
class Son :public Base {
public:
    int m_D;
};
void test01()
{
    Son s1;
    cout << sizeof(s1) << endl;
}
int main()
{
    //父类中所有的非静态成员属性都会被子类继承下去。
    test01();//结果为16.验证了m_C会被继承,但是子类无法访问。
}

结论:父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到

继承中构造和析构顺序

#include<iostream>
using namespace std;
#include<string>
class Base {
public:
    Base()
    {
        cout << "Base构造函数" << endl;
    }
    ~Base()
    {
        cout << "Base的析构函数" << endl;
    }
};
class Son :public Base {
public:
    Son()
    {
        cout << "Son的构造函数" << endl;
    }
    ~Son()
    {
        cout << "Son的析构函数" << endl;
    }
};
int main()
{
    Son s1;
}

打印结果:
Base构造函数
Son的构造函数
Son的析构函数
Base的析构函数
结论:父类先构造,后析构,子类后构造,先析构。

继承中同名成员的处理

#include<iostream>
using namespace std;
#include<string>
class Base {
public:
    Base()
    {
        m_A = 100;
    }
    void func()
    {
        cout << "Base作用域下的func函数调用" << endl;
    }
    void func(int a)
    {
        cout << "Base作用域下的func(int a)函数调用" << endl;
    }
    int m_A;
};
class Son :public Base
{
public:
    Son()
    {
        m_A = 200;
    }
    int m_A;
    void func()
    {
        cout << "Son作用域下的func函数调用" << endl;
    }
};
void test()
{
    Son s;
    cout << s.m_A << endl;
    //如果通过子类对象访问父类成员,需要加作用域
    cout << s.Base::m_A << endl;

}
void test1()
{
    Son s;
    s.func();
    s.Base::func();
    //如果子类出现了与父类同名成员函数,子类会隐藏掉父类所有的同名成员函数。
    s.Base::func(100);
}
int main()
{
    test1();
}

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

继承中同名静态成员的处理

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员直接访问即可
  • 访问父类同名成员需要加作用域
#include<iostream>
using namespace std;
#include<string>
class Base {
public:
    static int m_A;
    static void func()
    {
        cout << "Base static func调用" << endl;
    }
    static void func(int a)
    {
        cout << "Base static func(int a)调用" << endl;
    }
};
//静态成员变量类内声明类外初始化
int Base::m_A = 100;
class Son :public Base
{
public:
    static int m_A;
    static void func()
    {
        cout << "Son static func调用" << endl;
    }
};
int Son::m_A = 200;
void test()
{
    //通过对象访问
    Son s;
    cout << s.m_A << endl;
    cout << s.Base::m_A << endl;
    //通过类名访问
    cout << Son::m_A << endl;
    cout << Son::Base::m_A << endl;//通过子类访问父类的静态成员变量
}
void test1()
{
    //通过对象访问
    Son s;
    s.func();
    s.Base::func();
    //通过类名访问
    Son::func();
    Son::Base::func();//通过子类访问父类的静态成员函数
    Son::Base::func(10);
}
int main()
{
    test1();
}

多继承

C++允许一个类继承多个类
语法:class 子类:继承方式 父类1,继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分,所以实际开发不建议使用多继承。

#include<iostream>
using namespace std;
#include<string>
class Base1 {
public:
    Base1()
    {
        m_A = 100;
    }
    int m_A;
};
class Base2 {
public:
    Base2()
    {
        m_A = 200;
    }
    int m_A;
};
class Son :public Base1, public Base2 {
public:
    Son()
    {
        m_C = 300;
        m_D = 400;
    }
    int m_C;
    int m_D;
};
void test()
{
    Son s;
    cout << sizeof(s) << endl;
    //cout << s.m_A << endl;
    //报错,出现了二义性,父类1和2都有m_A变量。调用需要加作用域
    cout << s.Base1::m_A << endl;
    cout << s.Base2::m_A << endl;
}
int main()
{
    test();
}

菱形继承

菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
例如,B、C类同时继承了父类A,然后D类又多继承了B和C,那么B和C都会有A类里的某个变量,同时给了子类D,子类D就继承了两份这样的变量。此时就会出现一些问题。

#include<iostream>
using namespace std;
#include<string>
class Animal {
public:
    int m_A;
};
//在继承之前加上关键字virtual,表示虚继承,可以解决菱形继承的问题
//Animal类称为虚基类
class Sheep :virtual public Animal {

};
class Camel :virtual public Animal {
};
class Alpaca :public Sheep, public Camel {
};
void test()
{
    Alpaca alpaca;
    //alpaca.m_A = 18;报错,出现了二义性,需要加作用域
    alpaca.Sheep::m_A = 18;
    alpaca.Camel::m_A = 30;
    //当菱形继承时,两个父类拥有相同的数据,需要加作用域区分
    cout << alpaca.Sheep::m_A << endl;
    cout << alpaca.Camel::m_A << endl;
    //但是这份数据我们只需要一份,菱形继承造成了资源浪费。
}
int main()
{
    test();
}

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题,可以正常访问alpaca.m_A.并且和父类的m_A是共享的同一份数据。