class的实质

class内存分布

class 是一种特殊的 struct

  • 在内存中 class 依旧可以看作变量的集合
  • class 与 struct 遵循相同的内存对齐规则
  • class 中的成员函数与成员变量是分开存放的
    每个变量都有独立的成员变量(位于栈空间或堆空间或全局数据区)
    所有对象共享类中的成员函数(位于代码段)

image-20220227190209455

答案都是20个字节。

那么如果我们在class中添加函数呢?

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
#include <iostream>
#include <string>

using namespace std;

#pragma pack(4)
class A
{
int i;
int j;
char c;
double d;
public:
void print()
{
cout << "i = " << i << ", "
<< "j = " << j << ", "
<< "c = " << c << ", "
<< "d = " << d << endl;
}
};

struct B
{
int i2; //4 0
int j2; //4 4
char c2; //1 8
double d2; //8 12
};
#pragma pack()

int main()
{
A a;

cout << "sizeof(A) = " << sizeof(A) << endl; // 20 bytes
cout << "sizeof(a) = " << sizeof(a) << endl;
cout << "sizeof(B) = " << sizeof(B) << endl; // 20 bytes

a.print();

B* p = reinterpret_cast<B*>(&a);

p->i2 = 1;
p->j2 = 2;
p->c2 = 'c';
p->d2 = 3;

a.print();

p->i2 = 100;
p->j2 = 200;
p->c2 = 'C';
p->d2 = 3.14;

a.print();

return 0;
}

1
2
3
4
5
6
7
8
fengyun@ubuntu:~/share$ g++ test.cpp -o test
fengyun@ubuntu:~/share$ ./test
sizeof(A) = 20
sizeof(a) = 20
sizeof(B) = 20
i = 0, j = 0, c = `, d = -2.84627e+237
i = 1, j = 2, c = c, d = 3
i = 100, j = 200, c = C, d = 3.14

stuct A和class B内存排布相同。因此用指针类型转换后也可以直接访问成员变量。

运行时的对象退化为结构体的形式

  • 所有成员变量在内存中依次排布

  • 成员变量间可能存在内存空隙

  • 可以通过内存地址直接访问成员变量

  • 访问权限关键字在运行时失效

  • 类中的成员函数位于代码段中

  • 调用成员函数时对象地址作为参数隐式传递

  • 成员函数通过对象地址访问成员变量

  • C+ + 语法规则隐藏了对象地址的传递过程

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
#include <iostream>
#include <string>

using namespace std;

class Demo
{
int mi;
int mj;
public:
Demo(int i, int j)
{
mi = i;
mj = j;
}

int getI()
{
return mi;
}

int getJ()
{
return mj;
}

int add(int value)
{
return mi + mj + value;
}
};

int main()
{
Demo d(1, 2);

cout << "sizeof(d) = " << sizeof(d) << endl;
cout << "d.getI() = " << d.getI() << endl;
cout << "d.getJ() = " << d.getJ() << endl;
cout << "d.add(3) = " << d.add(3) << endl;

return 0;
}
1
2
3
4
5
fengyun@ubuntu:~/share$ ./test
sizeof(d) = 8
d.getI() = 1
d.getJ() = 2
d.add(3) = 6

那我们如果用C语言实现同样的逻辑,那该怎么写?

C语言没有class关键字,不可以在类内写成员函数。C语言写面向对象的代码,必须手动传入对象地址。C语言中没有private信息隐藏,我们因此定义typedef void Demo;Demo_Create返回的类型变为void*,我们无法直接通过返回的对象直接访问成员变量。

访问权限关键字在运行时失效,private 和 protected 的访问权限仅在编译时候有效。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
struct ClassDemo
{
int mi;
int mj;
};

Demo* Demo_Create(int i, int j)
{
struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));

if( ret != NULL )
{
ret->mi = i;
ret->mj = j;
}

return ret;
}

int Demo_GetI(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mi;
}

int Demo_GetJ(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mj;
}

int Demo_Add(Demo* pThis, int value)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mi + obj->mj + value;
}

void Demo_Free(Demo* pThis)
{
free(pThis);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
Demo* d = Demo_Create(1, 2); // Demo* d = new Demo(1, 2);

printf("sizeof(*d) = %d",sizeof(*d));
printf("d.mi = %d\n", Demo_GetI(d)); // d->getI();
printf("d.mj = %d\n", Demo_GetJ(d)); // d->getJ();
printf("Add(3) = %d\n", Demo_Add(d, 3)); // d->add(3);

// d->mi = 100;

Demo_Free(d);

return 0;
}
1
2
3
4
fengyun@ubuntu:~/share$ ./test
d.mi = 1
d.mj = 2
Add(3) = 6

最终打印效果一致。

小结

  • C+ + 中的类对象在内存布局上与结构体相同
  • 成员变量和成员函数在内存中分开存放
  • 访问权限关键字在运行时失效
  • 调用成员函数时对象地址作为参数隐式传递

继承对象模型

简单的C++继承对象模型

image-20220228103258626

image-20220228103520949

可以观察到大小是增加了4个字节,即int类型大小。那么内存排布是怎样的呢?

我选择临时定义一个Test结构体,Test结构体内存分布如下。

并且使用强制类型转换,把Derived *指针类型转换为Test *(p指针)

用p指针修改Test对象的成员变量值。改变过后调用Derived类内的成员函数print(),观察到这样竟然也修改Derived定义的结构体的成员变量的值。这说明Test结构体内存分布布局和Derived内存分布布局是相同的。

但是按照protected的成员变量在外界应当是访问不到的啊。这是因为访问权限关键字在运行时失效,在成员函数的内部有了这个隐藏的地址当然可以直接访问对象的成员变量。

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
#include <iostream>
#include <string>

using namespace std;
#progma pack(4)
class Demo
{
protected:
int mi;
int mj;
};

class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}

void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};

struct Test
{
int mi;
int mj;
int mk;
};
#progma pack()
int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl;
cout << "sizeof(Derived) = " << sizeof(Derived) << endl;

Derived d(1, 2, 3);
Test* p = reinterpret_cast<Test*>(&d);

cout << "Before changing ..." << endl;

d.print();

p->mi = 10;
p->mj = 20;
p->mk = 30;

cout << "After changing ..." << endl;

d.print();

return 0;
}

继承而来的内存排布和Test结构体是一样的。

1
2
3
4
5
6
7
fengyun@ubuntu:~/share$ ./test
sizeof(Demo) = 8
sizeof(Derived) = 12
Before changing ...
mi = 1, mj = 2, mk = 3
After changing ...
mi = 10, mj = 20, mk = 30

虚函数的继承对象模型

C+ + 多态的实现原理(相同的行为方式【调用语句】,不同的行为结果)

  • 当类中声明虚函数时 , 编译器会在类中生成一个虚函数表
  • 虚函数表是一个存储成员函数地址的数据结构
  • 虚函数表是由编译器自动生成与维护的
  • virtual 成员函数会被编译器放入虚函数表中
  • 存在虚函数时,每个对象中都有一个指向虚函数表的指针

C++的多态就是依靠虚函数

image-20220228105553757

当生成一个类对象的时候,编译器自动为这个对象“塞”一个指针变量,而这个指针指向创建的虚函数表。

image-20220228105803408

具体调用类内的函数时:p->add(v)

image-20220228105838842

具体调用过程:

image-20220228110152382

三次寻址,因此虚函数调用效率低于普通成员函数。

接着来验证一下带有虚函数的继承对象模型的内存分布:

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
#include <iostream>
#include <string>

using namespace std;

class Demo
{
protected:
int mi;
int mj;
public:
virtual void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << endl;
}
};

class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}

void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};

struct Test
{
void* p;
int mi;
int mj;
int mk;
};

int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl;
cout << "sizeof(Derived) = " << sizeof(Derived) << endl;

Derived d(1, 2, 3);
Test* p = reinterpret_cast<Test*>(&d);

cout << "Before changing ..." << endl;

d.print();

p->mi = 10;
p->mj = 20;
p->mk = 30;

cout << "After changing ..." << endl;

d.print();

return 0;
}

1
2
3
4
5
6
7
fengyun@ubuntu:~/share$ ./test
sizeof(Demo) = 16
sizeof(Derived) = 24
Before changing ...
mi = 1, mj = 2, mk = 3
After changing ...
mi = 10, mj = 20, mk = 30

ubantu64位的情况下,Demo类和Derived类都比之前多了8个字节,即一个指针的字节大小。

并且注意Test结构类型

1
2
3
4
5
6
7
struct Test
{
void* p;
int mi;
int mj;
int mk;
};

说明类的虚函数表指针默认放在类的成员变量之前

C语言实现继承与多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef _51_2_H_
#define _51_2_H_

typedef void Demo;
typedef void Derived;

Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);

Derived* Derived_Create(int i, int j, int k);
int Derived_GetK(Derived* pThis);
int Derived_Add(Derived* pThis, int value);

#endif
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "51-2.h"
#include "malloc.h"

static int Demo_Virtual_Add(Demo* pThis, int value);
static int Derived_Virtual_Add(Demo* pThis, int value);

struct VTable // 2. 定义虚函数表数据结构
{
int (*pAdd)(void*, int); // 3. 虚函数表里面存储什么???
};

struct ClassDemo
{
struct VTable* vptr; // 1. 定义虚函数表指针 ==》 虚函数表指针类型???
int mi;
int mj;
};

struct ClassDerived
{
struct ClassDemo d;
int mk;
};

static struct VTable g_Demo_vtbl =
{
Demo_Virtual_Add
};

static struct VTable g_Derived_vtbl =
{
Derived_Virtual_Add
};

Demo* Demo_Create(int i, int j)
{
struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));

if( ret != NULL )
{
ret->vptr = &g_Demo_vtbl; // 4. 关联对象和虚函数表
ret->mi = i;
ret->mj = j;
}

return ret;
}

int Demo_GetI(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mi;
}

int Demo_GetJ(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mj;
}

// 6. 定义虚函数表中指针所指向的具体函数
static int Demo_Virtual_Add(Demo* pThis, int value)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->mi + obj->mj + value;
}


// 5. 分析具体的虚函数!!!!
int Demo_Add(Demo* pThis, int value)
{

struct ClassDemo* obj = (struct ClassDemo*)pThis;

return obj->vptr->pAdd(pThis, value);
}

void Demo_Free(Demo* pThis)
{
free(pThis);
}

Derived* Derived_Create(int i, int j, int k)
{
struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));

if( ret != NULL )
{
ret->d.vptr = &g_Derived_vtbl;
ret->d.mi = i;
ret->d.mj = j;
ret->mk = k;
}

return ret;
}

int Derived_GetK(Derived* pThis)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;

return obj->mk;
}

static int Derived_Virtual_Add(Demo* pThis, int value)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;

return obj->mk + value;
}

int Derived_Add(Derived* pThis, int value)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;

return obj->d.vptr->pAdd(pThis, value);
}
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 "stdio.h"
#include "51-2.h"

void run(Demo* p, int v)
{
int r = Demo_Add(p, v);

printf("r = %d\n", r);
}

int main()
{
Demo* pb = Demo_Create(1, 2);
Derived* pd = Derived_Create(1, 22, 333);

printf("pb->add(3) = %d\n", Demo_Add(pb, 3));
printf("pd->add(3) = %d\n", Derived_Add(pd, 3));

run(pb, 3);
run(pd, 3);

Demo_Free(pb);
Demo_Free(pd);

return 0;
}

多态

多态的概念

只要是子类对象,我们需要默认调用子类重写过后的自定义的函数而不是执行父类继承的版本函数

面向对象中期望的行为
- 根据实际的对象类型判断如何调用重写函数
- 父类指针( 引用 ) 指向父类对象则调用父类中定义的函数
• 父类指针( 引用 ) 指向子类对象则调用子类中定义的重写函数

面向对象中的多态的概念

  • 根据实际的对象类型决定函数调用的具体目标
  • 同样的调用语句在实际运行时有多种不同的表现形态

image-20220227123619482

C+ + 语言直接支持多态的概念

  • 通过使用 virtual 关键字对多态进行支持
  • 被 virtual 声明的函数被重写后具有多态特性
  • 被 virtual 声明的函数叫做虚函数

由于继承的关系,父类中vitrual关键字也会被子类继承(我们在子类中写不写virtual都无所谓)

多态的意义

  1. 在程序运行过程中展现出动态的特性
  2. 函数重写必须多态实现(virtual),否则没有意义
  3. 多态是面向对象组件化程序设计的基础特性

理论中的概念
静态联编:在程序的编译期间就能确定具体的函数调用–比如:函数重载
动态联编:在程序实际运行后才能确定具体的函数调用–比如:函数重写

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
#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
virtual void func()
{
cout << "Parent::void func()" << endl;
}

virtual void func(int i)
{
cout << "Parent::void func(int i) : " << i << endl;
}

virtual void func(int i, int j)
{
cout << "Parent::void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
}
};

class Child : public Parent
{
public:
void func(int i, int j)
{
cout << "Child::void func(int i, int j) : " << i + j << endl;
}

void func(int i, int j, int k)
{
cout << "Child::void func(int i, int j, int k) : " << i + j + k << endl;
}
};

void run(Parent* p)
{
p->func(1, 2); // 展现多态的特性
// 动态联编
}


int main()
{
Parent p;

p.func(); // 静态联编
p.func(1); // 静态联编
p.func(1, 2); // 静态联编
//函数重载

cout << endl;

Child c;

c.func(1, 2); // 静态联编

cout << endl;

run(&p); // 动态联编
run(&c); // 动态联编

return 0;
}
1
2
3
4
5
6
7
8
9
10
fengyun@ubuntu:~/share$ g++ test.cpp -o test
fengyun@ubuntu:~/share$ ./test
Parent::void func()
Parent::void func(int i) : 1
Parent::void func(int i, int j) : (1, 2)

Child::void func(int i, int j) : 3

Parent::void func(int i, int j) : (1, 2)
Child::void func(int i, int j) : 3

小结

  • 函数重写只可能发生在父类与子类之间
  • 根据实际对象的类型确定调用的具体函数
  • virtual 关键字是 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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <string>

using namespace std;

class BaseA
{
int ma;
public:
BaseA(int a)
{
ma = a;
}
int getA()
{
return ma;
}
};

class BaseB
{
int mb;
public:
BaseB(int b)
{
mb = b;
}
int getB()
{
return mb;
}
};

class Derived : public BaseA, public BaseB
{
int mc;
public:
Derived(int a, int b, int c) : BaseA(a), BaseB(b)
{
mc = c;
}
int getC()
{
return mc;
}
void print()
{
cout << "ma = " << getA() << ", "
<< "mb = " << getB() << ", "
<< "mc = " << mc << endl;
}
};

int main()
{
cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 12

Derived d(1, 2, 3);

d.print();

cout << "d.getA() = " << d.getA() << endl;
cout << "d.getB() = " << d.getB() << endl;
cout << "d.getC() = " << d.getC() << endl;

cout << endl;

BaseA* pa = &d;
BaseB* pb = &d;

cout << "pa->getA() = " << pa->getA() << endl;
cout << "pb->getB() = " << pb->getB() << endl;

cout << endl;

void* paa = pa;
void* pbb = pb;


if( paa == pbb )
{
cout << "Pointer to the same object!" << endl;
}
else
{
cout << "Error" << endl;
}

cout << "pa = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "paa = " << paa << endl;
cout << "pbb = " << pbb << endl;

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fengyun@ubuntu:~/share$ ./test
sizeof(Derived) = 12
ma = 1, mb = 2, mc = 3
d.getA() = 1
d.getB() = 2
d.getC() = 3

pa->getA() = 1
pb->getB() = 2

Error
pa = 0x7ffd163e869c
pb = 0x7ffd163e86a0
paa = 0x7ffd163e869c
pbb = 0x7ffd163e86a0

通过多重继承得到的对象可能拥有”不 同 的 地 址“ !!
解决方案 : 无

image-20220228134732753

多重继承问题二:虚继承

多重继承可能产生冗余的成员

image-20220228134829820

当多重继承关系出现闭合时将产生数据冗余的问题 ! ! ! !

解决方案 : 虚继承

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
#include <iostream>
#include <string>

using namespace std;

class People
{
string m_name;
int m_age;
public:
People(string name, int age)
{
m_name = name;
m_age = age;
}
void print()
{
cout << "Name = " << m_name << ", "
<< "Age = " << m_age << endl;
}
};

class Teacher : virtual public People
{
public:
Teacher(string name, int age) : People(name, age)
{
}
};

class Student : virtual public People
{
public:
Student(string name, int age) : People(name, age)
{
}
};

class Doctor : public Teacher, public Student
{
public:
Doctor(string name, int age) : Teacher(name, age), Student(name, age), People(name, age)
{
}
};

int main()
{
Doctor d("fengyun", 20);

d.print();

return 0;
}

虚继承:

  • 中间层父类不再关心顶层父类的初始化
  • 虚继承能够解决数据冗余问题
  • 最终子类必须直接调用顶层父类的构造函数

问 题 :
当架构设计中需要继承时,无法确定使用直接继承还是虚继承

多重继承问题三:多个虚函数表

image-20220228140551207

image-20220228141157545

需要进行强制类型转换时 , C+ + 中推荐使用新式类型转换关键字 ! !
解决方案 : dynamic_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
#include <iostream>
#include <string>

using namespace std;

class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
};

class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
};

class Derived : public BaseA, public BaseB
{

};

int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbe = (BaseB*)pa; // oops!!
BaseB* pbc = dynamic_cast<BaseB*>(pa);

cout << "sizeof(d) = " << sizeof(d) << endl;

cout << "Using pa to call funcA()..." << endl;

pa->funcA();

cout << "Using pb to call funcB()..." << endl;

pb->funcB();

cout << "Using pbc to call funcB()..." << endl;

pbc->funcB();

cout << endl;

cout << "pa = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "pbe = " << pbe << endl;
cout << "pbc = " << pbc << endl;

return 0;
}

工程中的多重继承

单继承某个类 + 实现(多个) 接口

image-20220228142413068

一些有用的工程建议

  • 先继承自一个父类,然后实现多个接口
  • 父类中提供equal()成员函数
  • equal()成员函数用于判断指针是否指向当前对象
  • 与多重继承相关的强制类型转换用dynamic_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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <string>

using namespace std;

class Base
{
protected:
int mi;
public:
Base(int i)
{
mi = i;
}
int getI()
{
return mi;
}
bool equal(Base* obj)
{
return (this == obj);
}
};

class Interface1
{
public:
virtual void add(int i) = 0;
virtual void minus(int i) = 0;
};

class Interface2
{
public:
virtual void multiply(int i) = 0;
virtual void divide(int i) = 0;
};

class Derived : public Base, public Interface1, public Interface2
{
public:
Derived(int i) : Base(i)
{
}
void add(int i)
{
mi += i;
}
void minus(int i)
{
mi -= i;
}
void multiply(int i)
{
mi *= i;
}
void divide(int i)
{
if( i != 0 )
{
mi /= i;
}
}
};

int main()
{
Derived d(100);
Derived* p = &d;
Interface1* pInt1 = &d;
Interface2* pInt2 = &d;

cout << "p->getI() = " << p->getI() << endl; // 100

pInt1->add(10);
pInt2->divide(11);
pInt1->minus(5);
pInt2->multiply(8);

cout << "p->getI() = " << p->getI() << endl; // 40

cout << endl;

cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
Base()
{
cout << "Base()" << endl;

func();
}

virtual void func()
{
cout << "Base::func()" << endl;
}

virtual ~Base()
{
func();

cout << "~Base()" << endl;
}
};


class Derived : public Base
{
public:
Derived()
{
cout << "Derived()" << endl;

func();
}

virtual void func()
{
cout << "Derived::func()" << endl;
}

~Derived()
{
func();

cout << "~Derived()" << endl;
}
};


int main()
{
Base* p = new Derived();

// ...

delete p;

return 0;
}

如果不将析构函数设为虚函数,p指针类型是Base*,delete p将会执行Base的析构函数

构造函数中是否可以发生多态?
析构函数中是否可以发生多态?

  • 构造函数中不可能发生多态行为,只调用当前类中的版本
    在构造函数执行时 , 虚函数表指针未被正确初始化
  • 析构函数中不可能发生多态行为,只调用当前类中的版本
    在析构函数执行时 , 虚函数表指针已经被销毁