C++ 继承特性
1. 基本语法
继承用于让一个类(子类/派生类)自动拥有另一个类(父类/基类)的成员。
class 父类名 { /* ... */ };
class 子类名 : 继承方式 父类名 { /* ... */ };
2. 继承方式
C++支持三种继承方式:
继承方式 | 语法 | 说明 |
---|---|---|
public | class B : public A | 基类public/protected成员在子类中保持public/protected |
protected | class B : protected A | 基类public/protected成员在子类中变为protected |
private | class B : private A | 基类public/protected成员在子类中变为private |
3. 继承中的对象模型
• 子类对象包含父类的所有成员变量
(但不包括父类的构造、析构、赋值等特殊成员函数)。
• 父类的私有成员在子类中存在,但不可直接访问
。
4. 构造和析构顺序
• 构造顺序:先调用父类
构造函数,再调用子类
构造函数。
• 析构顺序:先调用子类
析构函数,再调用父类
析构函数。
示例:
class Base {
public:Base() { std::cout << "Base构造" << std::endl; }~Base() { std::cout << "Base析构" << std::endl; }
};
class Derived : public Base {
public:Derived() { std::cout << "Derived构造" << std::endl; }~Derived() { std::cout << "Derived析构" << std::endl; }
};
// 输出顺序:Base构造 → Derived构造 → Derived析构 → Base析构
5. 同名成员处理
• 子类和父类有同名成员时,子类成员会隐藏父类成员
。
• 可用作用域运算符访问
父类成员:子类对象.父类名::成员名
示例:
class Base { public: int x = 1; };
class Derived : public Base { public: int x = 2; };
Derived d;
std::cout << d.x << std::endl; // 输出2
std::cout << d.Base::x << std::endl; // 输出1
6. 同名静态成员处理
• 静态成员同样遵循“就近原则
”,子类同名静态成员会隐藏父类同名静态成员
。
• 可用作用域运算符访问父类静态成员:子类名::父类名::静态成员名
示例:
class Base { public: static int val; };
int Base::val = 100;
class Derived : public Base { public: static int val; };
int Derived::val = 200;
std::cout << Derived::val << std::endl; // 输出200
std::cout << Derived::Base::val << std::endl;// 输出100
7. 继承语法
• 单继承:class B : public A {}
• 多继承:class C : public A, public B {}
8. 菱形继承问题及解决方法
问题描述:
当一个类从两个基类
继承,而这两个基类又有共同的基类
时,会出现“菱形继承”,导致基类成员在最底层子类中有两份拷贝
,产生二义性。
示例:
class A { public: int x; };
class B : public A {};
class C : public A {};
class D : public B, public C {}; // D中有两份A
解决方法:
使用虚继承
,让共同基类只保留一份:
class A { public: int x; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // D中只有一份A
虚继承的实现细节:
• 编译器通过在子类对象中引入虚基类指针(vbptr)
和虚基类表(vbtable)
来实现虚继承。
• 每个虚继承的子类对象中包含指向虚基类的指针,确保无论通过哪条继承路径,最终都指向同一份基类子对象。
• 虚继承会略微增加对象的内存开销和访问基类成员的复杂度(需间接寻址)。
总结
• 继承让子类复用父类成员,支持多种继承方式。
• 构造/析构顺序:先父后子,析构反之。
• 同名成员、静态成员可用作用域区分。
• 菱形继承用虚继承解决二义性和冗余。