对象的自恰性:对于同样的函数调用,每个对象会做出自己恰当的响应
xxxxxxxxxx
531// selfconst.cpp
2
3// 对于同样的函数调用,每个对象会做出自己恰当的响应
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 void draw(void) const {
14 cout << "Shape(" << x << ',' << y << ')' << endl;
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 void draw(void) const {
26 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
27 }
28
29private:
30 double w, h;
31};
32
33class Circle : public Shape {
34public:
35 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
36
37 void draw(void) const {
38 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
39 }
40
41private:
42 double r;
43};
44
45int main(void) {
46 Rect rect(1, 2, 3, 4);
47 rect.draw();
48
49 Circle circle(5, 6, 7);
50 circle.draw();
51
52 return 0;
53}
指针游戏
通过指向子类对象的基类指针调用函数
xxxxxxxxxx
611// bdb.cpp
2
3// 通过指向子类对象的基类指针调用函数
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 void draw(void) const {
14 cout << "Shape(" << x << ',' << y << ')' << endl;
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 void draw(void) const {
26 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
27 }
28
29private:
30 double w, h;
31};
32
33class Circle : public Shape {
34public:
35 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
36
37 void draw(void) const {
38 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
39 }
40
41private:
42 double r;
43};
44
45int main(void) {
46 // 通过基类类型的指针,只能调用基类的成员函数,
47 // 虽然该指针实际指向的,是一个子类类型的对象
48
49 Rect rect(1, 2, 3, 4);
50 Shape* pshape = ▭
51 pshape->draw();
52
53 // 通过基类类型的引用,只能调用基类的成员函数,
54 // 虽然该引用实际引用的,是一个子类类型的对象
55
56 Circle circle(5, 6, 7);
57 Shape& rshape = circle;
58 rshape.draw();
59
60 return 0;
61}
通过指向基类对象的子类指针调用函数
xxxxxxxxxx
611// dbd.cpp
2
3// 通过指向基类对象的子类指针调用函数
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 void draw(void) const {
14 cout << "Shape(" << x << ',' << y << ')' << endl;
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 void draw(void) const {
26 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
27 }
28
29private:
30 double w, h;
31};
32
33class Circle : public Shape {
34public:
35 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
36
37 void draw(void) const {
38 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
39 }
40
41private:
42 double r;
43};
44
45int main(void) {
46 // 通过指向基类对象的子类指针,虽然可以调用子类的成员函数,
47 // 但凡是涉及到对子类特有部分的访问,其结果都是不可预知的
48
49 Shape shape1(1, 2);
50 Rect* rect = static_cast<Rect*>(&shape1);
51 rect->draw();
52
53 // 通过引用基类对象的子类引用,虽然可以调用子类的成员函数,
54 // 但凡是涉及到对子类特有部分的访问,其结果都是不可预知的
55
56 Shape shape2(5, 6);
57 Circle& circle = static_cast<Circle&>(shape2);
58 circle.draw();
59
60 return 0;
61}
通过指向子类对象的基类指针调用子类的函数
xxxxxxxxxx
711// bdd.cpp
2
3// 通过指向子类对象的基类指针调用子类的函数
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 void draw(void) const {
14 cout << "Shape(" << x << ',' << y << ')' << endl;
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 void draw(void) const {
26 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
27 }
28
29 double area(void) const {
30 return w * h;
31 }
32
33private:
34 double w, h;
35};
36
37class Circle : public Shape {
38public:
39 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
40
41 void draw(void) const {
42 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
43 }
44
45 double area(void) const {
46 return 3.14159 * r * r;
47 }
48
49private:
50 double r;
51};
52
53int main(void) {
54 // 通过指向子类对象的基类指针,无法调用子类的成员函数,
55 // 限制其能力的是它本身的类型,而非其所指向对象的类型,
56
57 Rect rect(1, 2, 3, 4);
58 Shape* pshape = ▭
59 // cout << pshape->area() << endl; // 编译错误
60 cout << static_cast<Rect*>(pshape)->area() << endl; // 向下造型
61
62 // 通过引用子类对象的基类引用,无法调用子类的成员函数,
63 // 限制其能力的是它本身的类型,而非其所引用对象的类型
64
65 Circle circle(5, 6, 7);
66 Shape& rshape = circle;
67 // cout << rshape.area() << endl; // 编译错误
68 cout << static_cast<Circle&>(rshape).area() << endl; // 向下造型
69
70 return 0;
71}
基于虚函数的运行时多态:
基类中带有virtual关键字的成员函数为虚函数
子类中的某个成员函数,如果与基类中的虚函数拥有完全相同的函数原型,那么该成员函数也是一个虚函数,并对基类中的版本构成覆盖
通过一个指向子类对象的基类指针,或一个引用子类对象的基类引用,调用基类中的虚函数,实际被执行的是子类中的覆盖版本,而非基类中的原始版本
这种使同一种类型表现出多种行为特征的语言特性,称为多态性
xxxxxxxxxx
841// bdv.cpp
2
3// 通过指向子类对象的基类指针调用虚函数
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 // 基类中带有virtual关键字的成员函数为虚函数
14
15 virtual void draw(void) const {
16 cout << "Shape(" << x << ',' << y << ')' << endl;
17 }
18
19protected:
20 double x, y;
21};
22
23class Rect : public Shape {
24public:
25 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
26
27 // 子类中的某个成员函数,如果与基类中的虚函数拥有完全相同的函数
28 // 原型,那么该成员函数也是一个虚函数,并对基类中的版本构成覆盖
29
30 void draw(void) const {
31 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
32 }
33
34private:
35 double w, h;
36};
37
38class Circle : public Shape {
39public:
40 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
41
42 // 子类中的某个成员函数,如果与基类中的虚函数拥有完全相同的函数
43 // 原型,那么该成员函数也是一个虚函数,并对基类中的版本构成覆盖
44
45 void draw(void) const {
46 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
47 }
48
49private:
50 double r;
51};
52
53void drawShape(const Shape* shape) {
54 shape->draw(); // 同一种类型表现出多种行为特征,谓之多态
55}
56
57void drawShapes(const Shape* shapes[]) {
58 for (size_t i = 0; shapes[i]; ++i)
59 shapes[i]->draw(); // 同一种类型表现出多种行为特征,谓之多态
60}
61
62int main(void) {
63 // 通过一个指向子类对象的基类指针,调用基类中的虚函数,
64 // 实际被执行的是子类中的覆盖版本,而非基类中的原始版本
65
66 Rect rect(1, 2, 3, 4);
67 Shape* pshape = ▭
68 pshape->draw();
69
70 // 通过一个引用子类对象的基类引用,调用基类中的虚函数,
71 // 实际被执行的是子类中的覆盖版本,而非基类中的原始版本
72
73 Circle circle(5, 6, 7);
74 Shape& rshape = circle;
75 rshape.draw();
76
77 drawShape(&rect);
78 drawShape(&circle);
79
80 const Shape* shapes[] {&rect, &circle, NULL};
81 drawShapes(shapes);
82
83 return 0;
84}
对基类中的虚函数,子类可以用一个具有相同签名(即函数名相同,参数表相同,常限定相同)的成员函数进行覆盖。为使覆盖有效需要满足以下条件:
如果基类中的虚函数返回一个基本类型的数据,那么该函数在子类中的覆盖版本也必须返回相同类型的数据
如果基类中的虚函数返回一个类类型的指针或引用,那么该函数在子类中的覆盖版本可以返回其子类类型的指针或引用——类型协变
无论基类中的虚函数位于公有、保护还是私有部分,该函数在子类中的覆盖版本都可以出现在包括公有、保护及私有在内的任何部分
xxxxxxxxxx
741// override.cpp
2
3// 虚函数覆盖需要满足的条件
4
5
6
7using namespace std;
8
9class X {};
10class Y : public X {};
11
12class A {
13public:
14 virtual int foo(int) {
15 cout << "A::foo() invoked" << endl;
16 return 0;
17 }
18
19 virtual X* bar(int) {
20 cout << "A::bar() invoked" << endl;
21 return NULL;
22 }
23
24 virtual int hum(int) {
25 cout << "A::hum() invoked" << endl;
26 return 0;
27 }
28};
29
30class B : public A
31{
32public:
33 // 不构成覆盖,常限定不同
34 int foo(int) const {
35 cout << "B::foo(1) invoked" << endl;
36 return 0;
37 }
38
39 // 不构成覆盖,参数表不同
40 int foo(int, bool) {
41 cout << "B::foo(2) invoked" << endl;
42 return 0;
43 }
44
45 // 编译错误,返回类型不同
46 // bool foo(int) {
47 // cout << "B::foo(3) invoked" << endl;
48 // return true;
49 // }
50
51 // 构成覆盖,返回类型是指针或引用,且为基类版本返回类型的子类
52 Y* bar(int) {
53 cout << "B::bar() invoked" << endl;
54 return NULL;
55 }
56
57private:
58 // 构成覆盖,访问控制属性与虚函数覆盖无关
59 int hum(int) {
60 cout << "B::hum() invoked" << endl;
61 return 0;
62 }
63};
64
65int main(void) {
66 B b;
67 A* a = &b;
68
69 a->foo(0);
70 a->bar(0);
71 a->hum(0);
72
73 return 0;
74}
xxxxxxxxxx
491// ptref.cpp
2
3// 多态性必须借助指针或引用才能表现出来
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 virtual void draw(void) const {
14 cout << "Shape(" << x << ',' << y << ')' << endl;
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 void draw(void) const {
26 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
27 }
28
29private:
30 double w, h;
31};
32
33int main(void) {
34 Rect rect(1, 2, 3, 4);
35
36 // 通过指针调用虚函数——多态
37 Shape* pshape = ▭
38 pshape->draw();
39
40 // 通过引用调用虚函数——多态
41 Shape& rshape = rect;
42 rshape.draw();
43
44 // 通过对象调用虚函数——非多态
45 Shape shape = rect; // 对象截切
46 shape.draw();
47
48 return 0;
49}
xxxxxxxxxx
561// this.cpp
2
3// 子类调基类靠继承,基类调子类靠多态
4
5
6
7using namespace std;
8
9class Base {
10public:
11 Base(void) {
12 // 在构造函数中调用虚函数没有多态性,因为这时子类对象
13 // 的特有部分尚未初始化,调用子类的覆盖版本是有风险的
14
15 bar();
16 }
17
18 ~Base(void) {
19 // 在析构函数中调用虚函数没有多态性,因为这时子类对象
20 // 的特有部分已经被析构,调用子类的覆盖版本是有风险的
21
22 bar();
23 }
24
25 void foo(void) const {
26 // 虽然没有显式地通过指向子类对象的基类指针,或引用子
27 // 类对象的基类引用,调用基类中的虚函数,但实际上是通
28 // 过this指针完成的函数调用,而this指针就是一个指向子
29 // 类对象的基类指针,表现出多态性,即基类调子类靠多态
30
31 bar(); // this->bar();
32 }
33
34 virtual void bar(void) const {
35 cout << "Base::bar()" << endl;
36 }
37};
38
39class Derived : public Base {
40public:
41 void bar(void) const {
42 cout << "Derived::bar()" << endl;
43 }
44
45 void hum(void) const {
46 foo(); // 子类调基类靠继承
47 }
48};
49
50int main (void) {
51 Derived d;
52
53 d.hum();
54
55 return 0;
56}
全局函数形式的操作符函数,无法通过虚函数展现其多态性
成员函数形式的操作符函数,和其它成员函数一样,可被声明为虚函数,并表现出多态性
xxxxxxxxxx
551// virop.cpp
2
3// 操作符函数的多态性
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 virtual ostream& operator>>(ostream& os) const {
14 return os << "Shape(" << x << ',' << y << ')';
15 }
16
17protected:
18 double x, y;
19};
20
21class Rect : public Shape {
22public:
23 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
24
25 ostream& operator>>(ostream& os) const {
26 return os << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')';
27 }
28
29private:
30 double w, h;
31};
32
33class Circle : public Shape {
34public:
35 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
36
37 ostream& operator>>(ostream& os) const {
38 return os << "Circle(" << x << ',' << y << ',' << r << ')';
39 }
40
41private:
42 double r;
43};
44
45int main(void) {
46 Rect rect(1, 2, 3, 4);
47 Shape* pshape = ▭
48 *pshape >> cout << endl;
49
50 Circle circle(5, 6, 7);
51 Shape& rshape = circle;
52 rshape >> cout << endl;
53
54 return 0;
55}
在成员函数声明的末尾加上“=0”,即将其声明为纯虚函数
纯虚函数亦称抽象函数或抽象方法,代表类的抽象行为,无需亦无法给出函数定义
至少拥有一个纯虚函数的类称为抽象类,抽象类表示某种抽象概念,不应亦不能实例化为对象
抽象类的子类,如果没有为基类中的所有纯虚函数提供覆盖实现,那么它也是一个抽象类
全部由纯虚函数构成的抽象类称为纯抽象类或接口类
xxxxxxxxxx
551// abstract.cpp
2
3// 纯虚函数、抽象类和纯抽象类
4
5
6
7using namespace std;
8
9class Shape {
10public:
11 Shape(double x, double y) : x(x), y(y) {}
12
13 virtual void draw(void) const = 0;
14
15protected:
16 double x, y;
17};
18
19class Rect : public Shape {
20public:
21 Rect(double x, double y, double w, double h) : Shape(x, y), w(w), h(h) {}
22
23 void draw(void) const {
24 cout << "Rect(" << x << ',' << y << ',' << w << ',' << h << ')' << endl;
25 }
26
27private:
28 double w, h;
29};
30
31class Circle : public Shape {
32public:
33 Circle(double x, double y, double r) : Shape(x, y), r(r) {}
34
35 void draw(void) const {
36 cout << "Circle(" << x << ',' << y << ',' << r << ')' << endl;
37 }
38
39private:
40 double r;
41};
42
43int main(void) {
44 Rect rect(1, 2, 3, 4);
45 Shape* pshape = ▭
46 pshape->draw();
47
48 Circle circle(5, 6, 7);
49 Shape& rshape = circle;
50 rshape.draw();
51
52 // Shape shape(8, 9); // 编译错误
53
54 return 0;
55}
xxxxxxxxxx
1081// vptr.cpp
2
3// 虚函数表
4
5
6
7
8
9using namespace std;
10
11typedef void (*VFUN)(void*);
12typedef VFUN* VPTR;
13
14class A {
15public:
16 A(int n) : n(n) {
17 VPTR vptr = *(VPTR*)this;
18 cout << "A::A() : " << vptr << "->[" <<
19 (void*)vptr[0] << ',' <<
20 (void*)vptr[1] << ']' << endl;
21 }
22
23 ~A(void) {
24 VPTR vptr = *(VPTR*)this;
25 cout << "A::~A() : " << vptr << "->[" <<
26 (void*)vptr[0] << ',' <<
27 (void*)vptr[1] << ']' << endl;
28 }
29
30 virtual void foo(void) {
31 cout << "A::foo() : " << n << endl;
32 }
33
34 virtual void bar(void) {
35 cout << "A::bar() : " << n << endl;
36 }
37
38protected:
39 int n;
40};
41
42class B : public A {
43public:
44 B(int n) : A(n) {
45 VPTR vptr = *(VPTR*)this;
46 cout << "B::B() : " << vptr << "->[" <<
47 (void*)vptr[0] << ',' <<
48 (void*)vptr[1] << ']' << endl;
49 }
50
51 ~B(void) {
52 VPTR vptr = *(VPTR*)this;
53 cout << "B::~B() : " << vptr << "->[" <<
54 (void*)vptr[0] << ',' <<
55 (void*)vptr[1] << ']' << endl;
56 }
57
58 void foo(void) {
59 cout << "B::foo() : " << n << endl;
60 }
61};
62
63int main (void) {
64 cout << "-------- 1 --------" << endl;
65
66 A a(100);
67 VPTR vptr = *(VPTR*)&a;
68 cout << "main() : " << vptr << "->[" <<
69 (void*)vptr[0] << ',' <<
70 (void*)vptr[1] << ']' << endl;
71 vptr[0](&a);
72 vptr[1](&a);
73
74 cout << "-------- 2 --------" << endl;
75
76 B b(200);
77 vptr = *(VPTR*)&b;
78 cout << "main() : " << vptr << "->[" <<
79 (void*)vptr[0] << ',' <<
80 (void*)vptr[1] << ']' << endl;
81 vptr[0](&b);
82 vptr[1](&b);
83
84 cout << "-------- 3 --------" << endl;
85
86 a = b;
87 vptr = *(VPTR*)&a;
88 cout << "main() : " << vptr << "->[" <<
89 (void*)vptr[0] << ',' <<
90 (void*)vptr[1] << ']' << endl;
91 vptr[0](&a);
92 vptr[1](&a);
93
94 cout << "-------- 4 --------" << endl;
95
96 memcpy(&a, &b, sizeof(a));
97 vptr = *(VPTR*)&a;
98 cout << "main() : " << vptr << "->[" <<
99 (void*)vptr[0] << ',' <<
100 (void*)vptr[1] << ']' << endl;
101 vptr[0](&a);
102 vptr[1](&a);
103 a.foo();
104 A& ra = a;
105 ra.foo();
106
107 return 0;
108}
xxxxxxxxxx
11A* pa = new B;
当编译器看到下面这条语句时:
xxxxxxxxxx
11pa->foo();
它并不知道pa所指向对象的真实身份,编译器所能做的就是用一段代码替代上面这行调用语句。这段代码将依次执行下列操作:
首先弄清指针pa所指向对象的真实身份
然后通过这个对象的虚函数表指针_vftbl访问其虚函数表,并找到与foo标识符相对应的虚函数代码入口地址
根据入口地址,调用该函数
虚拟函数表的存在必然会增加内存方面的开销,但这种开销事实上并不是太大
通过动态绑定调用多态函数较之通过静态绑定调用普通函数,要多出几个步骤。这几个步骤必然要消耗掉几个CPU周期,对整个程序的执行效率稍有影响
动态绑定妨碍编译器通过内联来优化代码
xxxxxxxxxx
641// PDF解析器,专注于解析PDF文件
2
3class PDFParser {
4public:
5 ...
6 void parse(const char* filename, ...) {
7 ...
8 drawText(...);
9 ...
10 drawRect(...);
11 ...
12 drawCircle(...);
13 ...
14 drawImage(...);
15 ...
16 }
17 ...
18protected:
19 ...
20 virtual void drawText(...) = 0;
21 virtual void drawRect(...) = 0;
22 virtual void drawCircle(...) = 0;
23 virtual void drawImage(...) = 0;
24 ...
25};
26
27// PDF渲染器,专注于绘制PDF文件中的可视元素
28
29class PDFRender : public PDFParser {
30public:
31 ...
32 void display(const char* filename, ...) {
33 ...
34 parse(filename, ...);
35 ...
36 }
37 ...
38protected:
39 ...
40 void drawText(...) {
41 ...
42 }
43
44 void drawRect(...) {
45 ...
46 }
47
48 void drawCircle(...) {
49 ...
50 }
51
52 void drawImage(...) {
53 ...
54 }
55 ...
56};
57
58int main(void) {
59 ...
60 PDFRender render(...);
61 render.display("cppprimier.pdf", ...);
62 ...
63 return 0;
64}
xxxxxxxxxx
621// dynamiccast.cpp
2
3// 将指向子类对象的基类指针动态转换(dynamic_cast)为子类指针
4
5
6
7using namespace std;
8
9class A {
10public:
11 virtual void foo(void) {}
12};
13
14class B : public A {}; // 多态继承
15class C : public A {}; // 多态继承
16class D {};
17
18int main(void) {
19 B b;
20
21 A* pa = &b;
22 cout << "pa=" << pa << endl;
23
24 B* pb = dynamic_cast<B*>(pa); // pa实际指向B类对象,转换成功
25 cout << "pb=" << pb << endl;
26
27 C* pc = dynamic_cast<C*>(pa); // pa没有指向C类对象,转换失败,安全
28 cout << "pc=" << pc << endl;
29
30 D* pd = dynamic_cast<D*>(pa); // pa没有指向D类对象,转换失败,安全
31 cout << "pd=" << pd << endl;
32
33 try {
34 A& ra = b;
35 D& rd = dynamic_cast<D&>(ra);
36 }
37 catch (bad_cast& ex) {
38 cout << ex.what() << endl;
39 }
40
41 pb = static_cast<B*>(pa); // B是A的子类,转换成功
42 cout << "pb=" << pb << endl;
43
44 pc = static_cast<C*>(pa); // C是A的子类,转换成功,危险!
45 cout << "pc=" << pc << endl;
46
47 // pd = static_cast<D*>(pa); // 编译错误,D不是A的子类,安全
48 // cout << "pd=" << pd << endl;
49
50 // 编译期、运行期均不检查,永远成功,危险!
51
52 pb = reinterpret_cast<B*>(pa);
53 cout << "pb=" << pb << endl;
54
55 pc = reinterpret_cast<C*>(pa);
56 cout << "pc=" << pc << endl;
57
58 pd = reinterpret_cast<D*>(pa);
59 cout << "pd=" << pd << endl;
60
61 return 0;
62}
xxxxxxxxxx
1071// typeid.cpp
2
3// typeid操作符
4
5
6
7
8using namespace std;
9
10template<typename T>
11void typeName(const char* lable) {
12 const type_info& ti = typeid(T);
13 cout << lable << ": " << ti.name() << endl;
14}
15
16template<typename T>
17void typeName(const T& t, const char* lable) {
18 const type_info& ti = typeid(t);
19 cout << lable << ": " << ti.name () << endl;
20}
21
22class X {};
23
24class Y : public X {}; // 普通继承
25
26class A {
27public:
28 virtual void foo(void) {}
29};
30
31class B : public A {}; // 多态继承
32
33int main(void) {
34 typeName<void>("void");
35 typeName<void*>("void*");
36 typeName<void**>("void**");
37
38 typeName<char>("char");
39 typeName<unsigned char>("unsigned char");
40 typeName<short>("short");
41 typeName<unsigned short>("unsigned short");
42 typeName<int>("int");
43 typeName<unsigned int>("unsigned int");
44 typeName<long>("long");
45 typeName<unsigned long>("unsigned long");
46 typeName<long long>("long long");
47 typeName<unsigned long long>("unsigned long long");
48 typeName<bool>("bool");
49 typeName<float>("float");
50 typeName<double>("double");
51 typeName<long double>("long double");
52
53 // 一个指向以函数指针为参数同时返回函数指针的函数的指针
54 typeName<long long*(*(*)(int*(*)(short*)))(char*)>(
55 "long long*(*(*)(int*(*)(short*)))(char*)");
56 // long long*(
57 // *(*)(
58 // int*(*)(short*)
59 // )
60 // )(char*)
61
62 typeName<X>("X");
63 typeName<Y>("Y");
64
65 Y y;
66 X* px = &y;
67 X& rx = y;
68
69 typeName(y, "y");
70 typeName(px, "px");
71 typeName(*px, "*px");
72 typeName(rx, "rx");
73
74 if (typeid(*px) == typeid(Y))
75 cout << "typeid(*px) == typeid(Y)" << endl;
76 else
77 cout << "typeid(*px) != typeid(Y)" << endl;
78
79 if (typeid(rx) == typeid(Y))
80 cout << "typeid(rx) == typeid(Y)" << endl;
81 else
82 cout << "typeid(rx) != typeid(Y)" << endl;
83
84 typeName<A>("A");
85 typeName<B>("B");
86
87 B b;
88 A* pa = &b;
89 A& ra = b;
90
91 typeName(b, "b");
92 typeName(pa, "pa");
93 typeName(*pa, "*pa");
94 typeName(ra, "ra");
95
96 if (typeid(*pa) == typeid(B))
97 cout << "typeid(*pa) == typeid(B)" << endl;
98 else
99 cout << "typeid(*pa) != typeid(B)" << endl;
100
101 if (typeid(ra) == typeid(B))
102 cout << "typeid(ra) == typeid(B)" << endl;
103 else
104 cout << "typeid(ra) != typeid(B)" << endl;
105
106 return 0;
107}
当delete操作符作用于一个指向子类对象的基类指针时,如果基类的析构函数没有被声明为虚函数,那么实际被调用的将仅仅是基类的析构函数,而基类的析构函数并不会自动调用子类的析构函数,因此实际被销毁的将仅仅是这个子类对象中基类子对象所创建的动态资源,而该子类对象自己的动态资源将失去被释放的机会,形成泄漏
xxxxxxxxxx
471// delbd.cpp
2
3// 通过基类指针析构子类对象的问题
4
5
6
7using namespace std;
8
9class X {
10public:
11 X(void) : data(new int[256]) {
12 cout << "X::X() invoked" << endl;
13 }
14
15 ~X(void) {
16 cout << "X::~X() invoked" << endl;
17 delete[] data;
18 }
19
20private:
21 int* data;
22};
23
24class Y : public X {
25public:
26 Y(void) : text(new char[1024]) {
27 cout << "Y::Y() invoked" << endl;
28 }
29
30 ~Y(void) {
31 cout << "Y::~Y() invoked" << endl;
32 delete[] text;
33 }
34
35private:
36 char* text;
37};
38
39int main(void) {
40 Y* y = new Y;
41 delete y; // 先调Y类的析构函数,再调X类的析构函数
42
43 X* x = new Y;
44 delete x; // 只调X类的析构函数,不调Y类的析构函数
45
46 return 0;
47}
当delete操作符作用于一个指向子类对象的基类指针时,如果基类的析构函数被声明为虚函数,那么实际被调用的将是子类的析构函数,而子类的析构函数在释放完子类对象自己的动态资源后,会自动调用基类的析构函数,完成对子类对象中基类子对象所创建动态资源的释放,避免泄漏
xxxxxxxxxx
481// virdes.cpp
2
3// 虚析构方案
4
5
6
7using namespace std;
8
9class X {
10public:
11 X(void) : data(new int[256]) {
12 cout << "X::X() invoked" << endl;
13 }
14
15 // 虚析构函数
16 virtual ~X(void) {
17 cout << "X::~X() invoked" << endl;
18 delete[] data;
19 }
20
21private:
22 int* data;
23};
24
25class Y : public X {
26public:
27 Y(void) : text(new char[1024]) {
28 cout << "Y::Y() invoked" << endl;
29 }
30
31 ~Y(void) {
32 cout << "Y::~Y() invoked" << endl;
33 delete[] text;
34 }
35
36private:
37 char* text;
38};
39
40int main(void) {
41 Y* y = new Y;
42 delete y; // 先调Y类的析构函数,再调X类的析构函数
43
44 X* x = new Y;
45 delete x; // 先调Y类的析构函数,再调X类的析构函数
46
47 return 0;
48}
子类的析构函数会自动调用基类的析构函数,但基类的析构函数不会自动调用子类的析构函数
将基类的析构函数声明为虚函数,可以保证子类的析构函数总被执行
在不定义析构函数的情况下,系统提供的缺省析构函数并非虚函数
有时必须为基类定义虚析构函数,即使它是个什么也不做的空函数,仅仅为了保证子类的析构函数被执行
xxxxxxxxxx
381// emptyvirdes.cpp
2
3// 空虚析构函数
4
5
6
7using namespace std;
8
9class X {
10public:
11 // 空虚析构函数
12 virtual ~X(void) {}
13
14private:
15 int data[256];
16};
17
18class Y : public X {
19public:
20 Y(void) : text(new char[1024]) {
21 cout << "Y::Y() invoked" << endl;
22 }
23
24 ~Y(void) {
25 cout << "Y::~Y() invoked" << endl;
26 delete[] text;
27 }
28
29private:
30 char* text;
31};
32
33int main(void) {
34 X* x = new Y;
35 delete x; // 一定会调Y类的析构函数
36
37 return 0;
38}
思考:构造函数可以是虚函数吗?静态成员函数可以是虚函数吗?