大纲 类的继承 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> using namespace std;class Parent {public : Parent (int a = 0 , int b = 0 ) { this ->a = a; this ->b = b; } void print () { cout << "a=" << this ->a << ", b=" << this ->b << endl; } public : int a; int b; }; class Child : public Parent {public : Child (int a = 0 , int b = 0 , int c = 0 ) { this ->a = a; this ->b = b; this ->c = c; } void echo () { cout << "a=" << this ->a << ", b=" << this ->b << ", c=" << this ->c << endl; } private : int c; }; int main () { Child child (1 , 2 , 3 ) ; child.print (); child.echo (); return 0 ; }
程序运行的输出结果如下:
派生类的构造过程 派生类对象构造的过程:
派生类先调用基类的构造函数,初始化从基类继承来的成员。 派生类后调用自己的构造函数,初始化派生类自己特有的成员。 派生类对象析构的过程:
派生类先调用自己的析构函数,释放派生类成员可能占用的外部资源(堆内存,文件等)。 派生类后调用基类的析构函数,释放派生类内存中从基类继承来的成员可能占用的外部资源(堆内存、文件等)。 总结
在 C++ 的类继承中,先构造的后析构,即后构造的先析构。
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 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std;class Base {public : Base (int data) : ma (data) { cout << "Base()" << endl; } ~Base () { cout << "~Base()" << endl; } protected : int ma; }; class Device : public Base {public : Device (int data) : Base (data), mb (data) { cout << "Device()" << endl; } ~Device () { cout << "~Device()" << endl; } private : int mb; }; int main () { Device d (2 ) ; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 Base() Device() ~Device() ~Base()
重载、重写、隐藏 重载关系
一组函数要重载,必须处在同一个作用域当中,且函数名字相同,但参数列表不同。 隐藏关系
在继承结构当中,派生类的同名成员会将基类的同名成员给隐藏掉,这里的隐藏是指作用域的隐藏。 重写关系
基类和派生类的函数,其函数名、返回值以及参数列表都相同,而且基类的方法是虚函数,那么派生类的方法就会被编译器自动处理成虚函数,它们之间成为重写(覆盖)关系。 案例代码一 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> using namespace std;class Base {public : Base (int data = 10 ) : ma (data) { } void show () { cout << "Base::show()" << endl; } void show (int a) { cout << "Base::show(int)" << endl; } private : int ma; }; class Device : public Base {public : Device (int data = 20 ) : Base (data), mb (data) { } void show () { cout << "Device::show()" << endl; } private : int mb; }; int main () { Device device; device.show (); device.Base::show (20 ); return 0 ; }
程序运行的输出结果如下:
1 2 Device::show() Base::show()
案例代码二 特别注意
在 C++ 的继承结构中进行上下的类型转换时,默认只支持从下(派生类)到上(基类)的类型的转换,不支持从上(基类)到下(派生类)的类型的转换。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <iostream> using namespace std;class Base {public : Base (int data = 10 ) : ma (data) { } void show () { cout << "Base::show()" << endl; } void show (int a) { cout << "Base::show(int)" << endl; } private : int ma; }; class Device : public Base {public : Device (int data = 20 ) : Base (data), mb (data) { } void show () { cout << "Device::show()" << endl; } private : int mb; }; void test01 () { cout << "=========== test01() ===========" << endl; Base base; Device device; base = device; base.show (); base.show (30 ); } void test02 () { cout << "=========== test02() ===========" << endl; Base base; Device device; Base *_base = &device; _base->show (); _base->show (40 ); } int main () { test01 (); test02 (); return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 6 =========== test01() =========== Base::show() Base::show(int) =========== test02() =========== Base::show() Base::show(int)
虚函数 虚函数的概念 (1) 当一个类里面定义了虚函数,那么在编译阶段,编译器会给这个类的类型产生一个唯一的 vftable
虚函数表,虚函数表中主要存储的内容是 RTTI
指针和虚函数的地址(如图所示 )。当程序运行时,每一张虚函数表都会加载到内存的 .rodata
区。
(2) 当一个类里面定义了虚函数,那么这个类定义的对象在其运行时,内存中开始的部分会多存储一个 vfptr
虚函数指针(占 4 字节大小),它指向相应类型的虚函数表 vftable
。一个类定义 N 个对象,它们的 vfptr
虚函数指针指向的都是同一张虚函数表。
(3) 如果派生类中的函数和从基类继承来的某个函数,其函数名、返回值、参数列表都相同,而且基类的函数是 virtual
关键字修饰的,那么派生类的这个函数会被编译器自动处理成虚函数。
(4) 一个类里面虚函数的个数,不影响类对象的内存大小(vfptr
虚函数指针永远只占用 4 个字节大小),影响的是虚函数表的大小。
(5) 在构造函数中调用虚函数,不会发生动态绑定。简而言之,在构造函数调用的函数,都是静态绑定。
哪些函数不能实现成虚函数
(1) 构造函数不能实现成虚函数。 (2) static
关键字修饰的函数不能实现成虚函数。 静态绑定(普通函数) 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <typeinfo> using namespace std;class Base {public : Base (int data = 10 ) : ma (data) { } void show () { cout << "Base::show()" << endl; } void show (int a) { cout << "Base::show(int a)" << endl; } private : int ma; }; class Device : public Base {public : Device (int data = 20 ) : Base (data), mb (data) { } void show () { cout << "Device::show()" << endl; } private : int mb; }; int main () { Device device (50 ) ; Base *pb = &device; pb->show (); cout << sizeof (Base) << endl; cout << sizeof (Device) << endl; cout << typeid (pb).name () << endl; cout << typeid (*pb).name () << endl; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 Base::show() 4 8 class Base * class Base
动态绑定(虚函数) 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <typeinfo> using namespace std;class Base {public : Base (int data = 10 ) : ma (data) { } virtual void show () { cout << "Base::show()" << endl; } virtual void show (int a) { cout << "Base::show(int a)" << endl; } private : int ma; }; class Device : public Base {public : Device (int data = 20 ) : Base (data), mb (data) { } void show () { cout << "Device::show()" << endl; } private : int mb; }; int main () { Device device (50 ) ; Base *pb = &device; pb->show (); cout << sizeof (Base) << endl; cout << sizeof (Device) << endl; cout << typeid (pb).name () << endl; cout << typeid (*pb).name () << endl; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 Device::show() 16 16 class Base * class Device
查看类的内存布局 在 Visual Studio 开发人员命令提示窗口内,可以使用以下命令查看类的内存布局信息,其中 YYY
是类的名称,xxx
是 C++ 源文件的名称。
1 cl /d1 reportSingleClassLayoutYYY xxx.cpp
在上述动态绑定(带虚函数)的代码中,Base 类的内存布局如下:
在上述动态绑定(带虚函数)的代码中,Device 类的内存布局如下:
虚函数调用深入理解 思考问题
在 C++ 中,是不是虚函数的调用一定就是动态绑定?
在 C++ 中,虚函数的调用不一定就是动态绑定,比如:
在类的构造函数中调用虚函数,只会发生静态绑定,而不会发生动态绑定。 类对象调用自己的成员函数(虚函数),只会发生静态绑定,而不会发生动态绑定。 通过指针或者引用变量调用虚函数,都会发生动态绑定,无论调用方是基类还是派生类,且无论被调用方是基类还是派生类。 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <iostream> using namespace std;class Base {public : Base (int data = 0 ) : ma (data) { } virtual void show () { cout << "Base::show()" << endl; } protected : int ma; }; class Device : public Base {public : Device (int data = 0 ) : Base (data), mb (data) { } void show () { cout << "Device::show()" << endl; } private : int mb; }; int main () { Base base; Device device; cout << "=====================" << endl; base.show (); device.show (); cout << "=====================" << endl; Base *bptr1 = &base; bptr1->show (); Base *bptr2 = &device; bptr2->show (); Base &b1 = base; base.show (); Base &b2 = device; b2.show (); cout << "=====================" << endl; Device *dptr = &device; dptr->show (); Device &d = device; d.show (); Device *dptr2 = (Device *) &base; dptr2->show (); return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 ===================== Base::show() Device::show() ===================== Base::show() Device::show() Base::show() Device::show() ===================== Device::show() Device::show() Base::show()
虚析构函数 虚析构函数的概念 (1) 析构函数可以是虚的,虚析构函数主要用于指引 delete
运算符正确析构动态对象。 (2) 构造函数不能是虚函数,因为建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。 (3) 虚析构函数使得在删除指向派生类对象的基类指针时,可以调用派生类的析构函数来实现释放派生类中堆内存的目的,从而防止内存泄漏。 (4) 当基类的指针(引用)指向在堆上 new
出来的派生类对象的时候,基类的析构函数必须实现成虚析构函数。在调用虚析构函数的时候,必定会发生动态绑定,否则会导致派生类的析构函数无法被调用。 虚析构函数的使用 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> using namespace std;class Base {public : Base (int data) : ma (data) { cout << "Base()" << endl; } virtual ~Base () { cout << "~Base()" << endl; } virtual void show () { cout << "Base::show()" << endl; } private : int ma; }; class Device : public Base {public : Device (int data) : Base (data), mb (data) { cout << "Device()" << endl; } ~Device () { cout << "~Device()" << endl; } private : int mb; }; int main () { Base *pb = new Device (10 ); pb->show (); delete pb; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 Base() Device() Base::show() ~Device() ~Base()
多态 多态的概念 静态(编译时期)的多态
动态(运行时期)的多态
在类的继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名函数(虚函数),基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名函数(虚函数)。 动态多态的底层是通过动态绑定来实现的,涉及虚函数指针(vfptr
)和虚函数表(vftable
)。 多态的案例代码一 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <iostream> #include <cstring> using namespace std;class Animal {public : Animal (string name) : _name(name) { } virtual void bark () { } protected : string _name; }; class Cat : public Animal {public : Cat (string name) : Animal (name) { } void bark () { cout << _name << " bark: miao miao" << endl; } }; class Dog : public Animal {public : Dog (string name) : Animal (name) { } void bark () { cout << _name << " bark: wang wang" << endl; } }; class Pig : public Animal {public : Pig (string name) : Animal (name) { } void bark () { cout << _name << " bark: heng heng" << endl; } }; void bark (Animal &animal) { animal.bark (); } int main () { Cat cat = Cat ("Cat" ); Dog dog = Dog ("Dog" ); Pig pig = Pig ("Pig" ); bark (cat); bark (dog); bark (pig); return 0 ; }
程序运行的输出结果如下:
1 2 3 Cat bark: miao miao Dog bark: wang wang Pig bark: heng heng
多态的案例代码二 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <iostream> #include <cstring> using namespace std;class Animal {public : Animal (string name) : _name(name) { } virtual void bark () { } protected : string _name; }; class Cat : public Animal {public : Cat (string name) : Animal (name) { } void bark () { cout << _name << " bark: miao miao" << endl; } }; class Dog : public Animal {public : Dog (string name) : Animal (name) { } void bark () { cout << _name << " bark: wang wang" << endl; } }; class Pig : public Animal {public : Pig (string name) : Animal (name) { } void bark () { cout << _name << " bark: heng heng" << endl; } }; void bark (Animal *animal) { animal->bark (); } int main () { Cat cat = Cat ("Cat" ); Dog dog = Dog ("Dog" ); Pig pig = Pig ("Pig" ); bark (&cat); bark (&dog); bark (&pig); return 0 ; }
程序运行的输出结果如下:
1 2 3 Cat bark: miao miao Dog bark: wang wang Pig bark: heng heng
纯虚函数和抽象类 纯虚函数和抽象类的概念 基本概念:
(a) 纯虚函数是一个在基类中声明的虚函数,且在基类中没有被定义,要求任何派生类都必须定义自己的版本。 (b) 纯虚函数为各派生类提供一个公共界面,可以实现接口的封装和设计、软件的模块功能划分等。 (c) 纯虚函数的声明形式: virtual 类型 函数名 ( 参数表 ) = 0;
。 (d) 一个拥有纯虚函数的基类,通常称之为 “抽象类”。 使用限制:
(a) 可以声明抽象类的指针和引用。 (b) 抽象类不能创建对象(实例化)。 (c) 抽象类不能作为函数的参数类型和返回值类型。 (d) 如果基类中存在纯虚函数,那么派生类必须实现所有的纯虚函数,否则这个派生类也是一个抽象类。 特别注意
抽象类可以有构造函数。尽管抽象类无法被直接实例化(因为它包含至少一个纯虚函数),但它仍然可以定义构造函数,用于初始化抽象类的成员变量或执行其他构造逻辑。这样,当抽象类被继承并通过派生类构造时,抽象类的构造函数会被调用。同理,抽象类也可以有析构函数。
纯虚函数和抽象类的案例一 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <iostream> using namespace std;class Figure {public : virtual double getArea () = 0 ; }; class Circle : public Figure {public : Circle (double r) { this ->r = r; } virtual double getArea () { double area = 3.14 * r * r; cout << "圆的面积: " << area << endl; return area; } private : double r; }; class Triangle : public Figure {public : Triangle (double a, double b) { this ->a = a; this ->b = b; } virtual double getArea () { double area = a * b / 2 ; cout << "三角形的面积: " << area << endl; return area; } private : double a; double b; }; class Square : public Figure {public : Square (double a, double b) { this ->a = a; this ->b = b; } virtual double getArea () { double area = a * b; cout << "四边形的面积: " << area << endl; return area; } private : double a; double b; }; void printArea (Figure* base) { base->getArea (); } int main () { Triangle Triangle (20 , 30 ) ; Square square (50 , 60 ) ; Figure* pBase = new Circle (5.3 ); pBase->getArea (); Figure& base = square; base.getArea (); printArea (&Triangle); return 0 ; }
程序运行的输出结果如下:
1 2 3 圆的面积: 88.2026 四边形的面积: 3000 三角形的面积: 300
纯虚函数和抽象类的案例二 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> #include <cstring> using namespace std;class Animal {public : Animal (string name) : _name(name) { cout << "Animal()" << endl; } ~Animal () { cout << "~Animal()" << endl; } virtual void bark () = 0 ; protected : string _name; }; class Cat : public Animal {public : Cat (string name) : Animal (name) { } void bark () { cout << _name << " bark: miao miao" << endl; } }; class Dog : public Animal {public : Dog (string name) : Animal (name) { } void bark () { cout << _name << " bark: wang wang" << endl; } }; class Pig : public Animal {public : Pig (string name) : Animal (name) { } void bark () { cout << _name << " bark: heng heng" << endl; } }; void bark (Animal &animal) { animal.bark (); } int main () { Cat cat = Cat ("Cat" ); Dog dog = Dog ("Dog" ); Pig pig = Pig ("Pig" ); bark (cat); bark (dog); bark (pig); return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 6 7 8 9 Animal() Animal() Animal() Cat bark: miao miao Dog bark: wang wang Pig bark: heng heng ~Animal() ~Animal() ~Animal()
虚继承与虚基类 虚继承与虚基类的概念 基本概念
虚继承的声明需要使用关键字 virtual
。 在 C++ 中,被虚继承的类通常称作为虚基类。 虚继承的底层是靠虚基类指针(vbptr
)和虚基类表(vbtable
)来实现。 如果一个派生类从多个基类继承,而这些基类又有一个共同的基类(公共基类),则在对该基类中声明的成员进行访问时,可能会产生二义性,需要使用虚继承来解决二义性问题。 如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象。要使这个公共基类在派生类中只产生一个子对象,必须对这个基类的继承声明为虚继承,使这个基类成为 虚基类
。 适用场景
虚继承只适用于有共同基类(公共基类)的多继承场景(比如菱形继承),如下图所示:
不适用场景对于 V
字形的多继承场景,虚继承是没办法解决二义性问题的,如下图所示:
虚继承与虚基类使用案例 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <iostream> using namespace std;class Base {public : Base (int x) { this ->x = x; cout << "Base 类的构造函数被调用" << endl; } void printX () { cout << "x = " << x << endl; } private : int x; }; class Base1 : virtual public Base {public : Base1 (int a, int x) : Base (x) { this ->a = a; } void printA () { cout << "a = " << a << endl; } private : int a; }; class Base2 : virtual public Base {public : Base2 (int b, int x) : Base (x) { this ->b = b; } void printB () { cout << "b = " << b << endl; } private : int b; }; class Base3 : public Base1, public Base2 {public : Base3 (int a, int b, int c, int x) : Base1 (a, x), Base2 (b, x), Base (x) { this ->c = c; } void printC () { cout << "c = " << c << endl; } private : int c; }; int main () { Base3 base (1 , 2 , 3 , 4 ) ; base.printA (); base.printB (); base.printC (); base.printX (); return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 Base 类的构造函数被调用 a = 1 b = 2 c = 3 x = 4
虚继承的类内存布局之一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std;class A {private : int ma; }; class B : virtual public A {private : int mb; }; int main () { cout << "size : " << sizeof (B) << endl; return 0 ; }
程序运行的输出结果如下:
在 Visual Studio 中查看上述 B 派生类的内存布局,可以得到以下结果,其中 vbptr
是虚基类指针(Virtual Base Ptr),而 vbtable
是虚基类表(Virtual Base Table)。
虚继承的类内存布局之二 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> using namespace std;class A {public : virtual void show () { cout << "call A::show()" << endl; } private : int ma; }; class B : virtual public A {public : void show () { cout << "call B::show()" << endl; } private : int mb; }; int main () { cout << "size : " << sizeof (B) << endl; B b; A &a = b; b.show (); return 0 ; }
程序运行的输出结果如下:
1 2 size : 32 call B::show()
在 Visual Studio 中查看上述 B 派生类的内存布局,可以得到以下结果;其中 vbptr
是虚基类指针(Virtual Base Ptr),而 vbtable
是虚基类表(Virtual Base Table);vfptr
是虚函数指针(Virtual Function Ptr),vftable
是虚函数表(Virtual Function Table)。
特别注意
基类指针(引用)指向派生类对象时,永远指向的是派生类中基类那部分数据的起始地址。
菱形继承的问题 菱形继承的概念 在 C++ 的多继承中,有一种特殊的继承结构,那就是菱形继承,如下图所示:
在 C++ 中,菱形继承会产生以下问题:
内存冗余问题 :基类的内容被重复存储,占用额外内存。成员重复问题 :基类的成员在子类中会出现多份,导致访问不明确(存在二义性)。构造与析构问题 :基类的构造函数或析构函数可能被调用多次,导致不一致或意外行为。多态冲突问题 :虚函数可能产生不明确的调用,导致多态行为失效。菱形继承问题的产生 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <iostream> using namespace std;class A {public : A (int data) : ma (data) { cout << "A()" << endl; } ~A () { cout << "~A()" << endl; } public : int ma; }; class B : public A {public : B (int data) : A (data), mb (data) { cout << "B()" << endl; } ~B () { cout << "~B()" << endl; } public : int mb; }; class C : public A {public : C (int data) : A (data), mc (data) { cout << "C()" << endl; } ~C () { cout << "~C()" << endl; } public : int mc; }; class D : public B, public C {public : D (int data) : B (data), C (data), md (data) { cout << "D()" << endl; } ~D () { cout << "~D()" << endl; } public : int md; }; int main () { D d (20 ) ; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 6 7 8 9 10 A() B() A() C() D() ~D() ~C() ~A() ~B() ~A()
从上述的执行结果可以发现基类 A 被构造了两次,这执行效率是比较低的;而且派生类 D 无法正常访问基类 A 中的成员变量 ma
,因为存在二义性。
菱形继承问题的解决 在 C++ 的多继承中,可以使用虚继承来解决菱形继承产生的问题。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <iostream> using namespace std;class A {public : A (int data) : ma (data) { cout << "A()" << endl; } ~A () { cout << "~A()" << endl; } public : int ma; }; class B : virtual public A {public : B (int data) : A (data), mb (data) { cout << "B()" << endl; } ~B () { cout << "~B()" << endl; } public : int mb; }; class C : virtual public A {public : C (int data) : A (data), mc (data) { cout << "C()" << endl; } ~C () { cout << "~C()" << endl; } public : int mc; }; class D : public B, public C {public : D (int data) : A (data), B (data), C (data), md (data) { cout << "D()" << endl; } ~D () { cout << "~D()" << endl; } public : int md; }; int main () { D d (20 ) ; cout << d.ma << endl; return 0 ; }
程序运行的输出结果如下:
1 2 3 4 5 6 7 8 9 A() B() C() D() 20 ~D() ~C() ~B() ~A()
从上述的执行结果可以发现基类 A 只构造了一次,而且派生类 D 可以正常访问基类 A 中的成员变量 ma
,因为不存在二义性。
C++ 的四种类型转换 在 C++ 中,有以下 4 种类型转换:
static_cast
:静态类型转换,如 int
转换成 char
dynamic_cast
:动态类型转换,主要用在类的继承结构中,可以支持 RTTI 类型识别的上下类型转换,如父类和子类之间的多态类型转换const_cast
:常量类型转换,用于赋予或者去除类型的 const
只读属性reinterpreter_cast
:重新解释类型(强制类型转换)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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <iostream> using namespace std;class Base {public : virtual void show () = 0 ; }; class Device1 : public Base {public : void show () { cout << "call Device1::show()" << endl; } }; class Device2 : public Base {public : void show () { cout << "call Device2::show()" << endl; } void print () { cout << "call Device2::print()" << endl; } }; void show (Base *base) { Device2 *pDevice2 = dynamic_cast <Device2 *>(base); if (pDevice2 == nullptr ) { base->show (); } else { pDevice2->print (); } } int main () { const int a = 10 ; int *p1 = const_cast <int * > (&a); cout << *p1 << endl; int b = 65 ; char c = static_cast <char >(b); cout << c << endl; int d = 20 ; int *p2 = reinterpret_cast <int *> (d); Device1 device1; Device2 device2; show (&device1); show (&device2); return 0 ; }
程序运行的输出结果如下:
1 2 3 4 10 A call Device1::show() call Device2::print()
虚函数高频面试题 虚函数高频面试题之一 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <iostream> using namespace std;class Animal {public : Animal (string name) : _name(name) { } virtual void bark () { } protected : string _name; }; class Cat : public Animal {public : Cat (string name) : Animal (name) { } void bark () { cout << _name << " bark: miao miao" << endl; } }; class Dog : public Animal {public : Dog (string name) : Animal (name) { } void bark () { cout << _name << " bark: wang wang" << endl; } }; int main () { Animal *p1 = new Cat ("Cat" ); Animal *p2 = new Dog ("Dog" ); int *p11 = (int *) p1; int *p22 = (int *) p2; int tmp = p11[0 ]; p11[0 ] = p22[0 ]; p22[0 ] = tmp; p1->bark (); p2->bark (); delete p1; delete p2; return 0 ; }
程序运行的输出结果如下:
1 2 Cat bark: wang wang Dog bark: miao miao
虚函数高频面试题之二 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 30 31 32 33 #include <iostream> using namespace std;class Base {public : virtual void show (int i = 10 ) { cout << "call Base::show i : " << i << endl; } }; class Device : public Base {public : void show (int i = 20 ) { cout << "call Device::show i : " << i << endl; } }; int main () { Base *b = new Device (); b->show (); delete b; return 0 ; }
程序运行的输出结果如下:
1 call Device::show i : 10
虚函数高频面试题之三 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 30 31 32 33 34 #include <iostream> using namespace std;class Base {public : virtual void show () { cout << "call Base::show()" << endl; } }; class Device : public Base {private : void show () { cout << "call Device::show()" << endl; } }; int main () { Base *b = new Device (); b->show (); delete b; 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> #include <cstring> using namespace std;class Base {public : Base () { cout << "call Base()" << endl; clear (); } virtual void show () { cout << "call Base::show()" << endl; } void clear () { memset (this , 0 , sizeof (*this )); } }; class Device : public Base {public : Device () { cout << "call Device()" << endl; } void show () { cout << "call Device::show()" << endl; } }; int main () { Base *bptr1 = new Base (); bptr1->show (); delete bptr1; Base *bptr2 = new Device (); bptr2->show (); delete bptr2; return 0 ; }