一、基类与派生类的概念
- 基类(父类):在继承关系中处于上层的类
- 派生类(子类):在继承关系中处于下层的类
class A;
class B;
class C:public A //C为A的子类,A为C的父类
{
};
class D:public A,public B //D为A和B的子类,A和B均为D的父类
{
};
二、类派生列表
- 派生类通过派生类列表来指出其从哪个(哪些)基类继承而来
类派生列表的使用规则:
- ①派生列表不能出现在类的声明时,只能在定义时,原因如下:
- 一条声明语句的目的是让程序知晓某个名字的存在已经改名字表示一个什么样的实体(如一个类、一个函数、或一个变量等)
class A; class B; //class B:public A; 错误 class A{}; class B:public A{}; //正确
- ②要继承的基类必须在本类之前定义而非声明,原因如下:
- 派生类必须知道其从基类继承而来的成员是什么,如果基类只是一个声明,那么派生类将无从知晓
class A; //声明 class B:public A{}; //错误 class A{};
- ③一个类不能派生其自身,原因和②是相同的
三、继承的基本特点
- ①一个类可以被多个类继承
- ②一个类也可以继承于多个类
四、单一继承
- 一个类只继承于一个类叫做单一继承
class A
{
};
class B :public A //单一继承
{
};
五、多重继承
- 一个类只继承于多个类叫做多重继承
class A{};
class B {};
class C :public B, public A //多重继承
{
};
六、多级继承
- 一个子类还可以作为另一个类的父类而派生出另一个子类。在宏观上叫做多级继承
class A{};
class B :public A
{};
class C :public B
{
};
七、菱形继承
- 概念:A作为基类,B和C都继承与A。最后一个类D又继承于B和C,这样形式的继承称为菱形继承
- 菱形继承的缺点:
- 数据冗余:在D中会保存两份A的内容
- 访问不明确(二义性):因为D不知道是以B为中介去访问A还是以C为中介去访问A,因此在访问某些成员的时候会发生二义性
- 缺点的解决:
- 数据冗余:通过下面“虚继承”技术来解决(见下)
- 访问不明确(二义性):通过作用域访问符::来明确调用。虚继承也可以解决这个问题
演示案例
class A { public: A(int a) :m_a(a) {} int getMa() { return m_a; } private: int m_a; }; class B :public A { public: B(int a, int b) :A(a), m_b(b) {} private: int m_b; }; class C :public A { public: C(int a, int c) :A(a), m_c(c) {} private: int m_c; }; class D :public B, public C { public: D(int a, int b, int c, int d) :B(a, b), C(a, c), m_d(d) {} void func() { /*错误,访问不明确 std::cout << getMa();*/ //正确,通过B访问getMa() std::cout << B::getMa(); } private: int m_d; };
八、虚继承
- 虚继承的作用:为了保证公共继承对象在创建时只保存一分实例
- 虚继承解决了菱形继承的两个问题:
- 数据冗余:顶级基类在整个体系中只保存了一份实例
- 访问不明确(二义性):可以不通过作用域访问符::来调用(原理就是因为顶级基类在整个体系中只保存了一份实例)
- 虚继承不常用,也不建议使用
class A
{
public:
A(int a) :m_a(a) {}
int getMa() { return m_a; }
private:
int m_a;
};
class B :virtual public A
{
public:
B(int a, int b) :A(a), m_b(b) {}
private:
int m_b;
};
class C :virtual public A
{
public:
C(int a, int c) :A(a), m_c(c) {}
private:
int m_c;
};
class D :virtual public B, virtual public C
{
public:
D(int a, int b, int c, int d) :B(a, b), C(a, c), m_d(d) {}
void func()
{
//都正确,因为A的内容在整个继承体系中只保存了一份
std::cout << getMa();
std::cout << A::getMa();
}
private:
int m_d;
};