3. 基类指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class UsbDevice {
};
class UsbHub : public UsbDevice {
}
int main() {
UsbHub hub;
UsbDevice& device1 = hub; // 基类引用指向派生类对象
UsbDevice* device2 = &hub; // 基类指针指向派生类对象
}
|
4. 使用using提权/降权
用于修饰类成员(从基类继承过来的成员),提升成员的权限,但是只能对基类的public和protected成员进行提权或降权操作,不能改变基类private成员的访问权限,因为它们在派生类中不可见。
1
2
3
4
5
6
7
8
9
10
11
12
|
class Base {
protected:
void internal() {}
};
class Derived : public Base {
public:
using Base::internal; // 将internal成员权限提升为public,原本为protected
};
Derived d;
d.internal();
|
5. 继承中的构造与析构
构造与析构不会被继承。
5.1. 构造
- 构造函数调用顺序: 基类 –> 派生类,与析构相反。
- 如果有多个基类,基类的构造是从左到右调用。
- 如果派生类没有指定调用基类的那个构造函数。就调用默认构造函数。
- 派生类只能调用
直接基类的构造函数,但是虚继承中不同。
1
2
3
4
5
6
7
|
class Derived : public Base {
public:
// 如果不指定调用基类的那个构成函数,就会调用基类的默认构造函数
Derived( <参数列表> ) : Base( <基类构造函数参数> ) {
// 派生类构造函数体
}
};
|
一个基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <iostream>
using namespace std;
class Base {
public:
Base(int x) {
cout << "Base constructor with x = " << x << endl;
}
};
class Derived : public Base {
public:
// 显式调用 Base(int)
Derived(int x, int y) : Base(x) {
cout << "Derived constructor with y = " << y << endl;
}
};
int main() {
Derived d(10, 20);
return 0;
}
|
多个基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <iostream>
using namespace std;
class A {
public:
A(int x) { cout << "A constructor with x = " << x << endl; }
int x;
};
class B {
public:
B(double y) { cout << "B constructor with y = " << y << endl; }
int y;
};
class Derived : public A, public B {
public:
// 按继承顺序调用 A 和 B
Derived(int x, double y) : A(x), B(y) {
cout << "Derived constructor" << endl;
}
};
int main() {
Derived d(10, 3.14);
return 0;
}
|
使用初始化列表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <iostream>
using namespace std;
class A {
public:
A(int x) { cout << "A constructor with x = " << x << endl; }
};
class B {
public:
B(double y) { cout << "B constructor with y = " << y << endl; }
};
class Derived : public A, public B {
private:
int z;
public:
// 按继承顺序调用基类构造函数,然后是初始化列表
Derived(int x, double y, int z) : A(x), B(y), z(z) {
cout << "Derived constructor with z = " << z << endl;
}
};
int main() {
Derived d(10, 3.14, 42);
}
|
5.2. 析构
- 调用顺序: 派生类 –> 基类, 与构造函数相反。
- 无需显式调用基类析构函数, C++ 编译器会自动调用基类的析构函数。
- 基类的析构函数必须声明为
虚函数, 这样才能通过基类的指针或引用调用派生类的析构函数,以安全释放内存。
- 将基类的析构函数声明为虚函数后,派生类的析构函数也会自动成为虚函数。
1
2
3
4
5
6
7
8
9
|
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
~Derived() override { /* 释放派生类资源 */ }
};
|
6. 虚继承
菱形继承如下:
1
2
3
4
5
|
A
/ \
B C
\ /
D
|
这种结构导致D通过B和C继承了两个独立的A实例,从而引发数据冗余和成员访问二义性。C++提供了虚拟继承机制,该机制下由派生类D来初始化虚基类A,也就说,由派生类D来调用A的构造函数,B和C调用A的构造函数是无效的,最终D中仅保留一份A的实例,B和C共享该实例。
C++标准库中的iostream类就是一个虚继承的实际应用案例。iostream从istream和ostream直接继承而来,而istream和ostream又都继承自一个共同的名为base_ios的类,是典型的菱形继承。此时istream和ostream必须采用虚继承,否则将导致iostream类中保留两份base_ios类的成员。

iostream是最终派生类,istream、ostream是直接基类,base_ios是间接基类(虚基类)。istream和ostream的公共成员声明在虚基类中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <iostream>
class A{
public:
A() { std::cout << "AAA"<< std::endl; }
};
class B : virtual public A{
public:
B() { std::cout << "BBB"<< std::endl; }
};
class C : virtual public A {
public:
C() { std::cout << "CCC" << std::endl; }
};
class D : public B, public C{
public:
D() { std::cout << "DDD"<< std::endl; }
};
int main()
{
D a;
}
|
上面代码输出:
如果没有virtual修饰,A的构造函数会被调用两次:
1
2
3
4
5
|
AAA ##
BBB
AAA ##
CCC
DDD
|
如果A没有成员(除了构造与析构),或者A有成员但是没有被访问,还是可以编译的,但是只要访问A的成员就会二义性。