inherit.docx
《inherit.docx》由会员分享,可在线阅读,更多相关《inherit.docx(11页珍藏版)》请在冰豆网上搜索。
inherit
inherit
成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字。
“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。
此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。
此时,基类的函数被隐藏(注意别与重写混淆)
隐藏可以通过:
:
来访问,覆盖就真的没了
可以安全地将一个指向基类成员的指针赋值给一个指向派生类的成员
的指针,反过来不行。
另外,可将一个指向派生类对象的指针赋值给一个指向基类得指针,
即基类指针可在不加显式转换的情况下指向派生类对象。
上述两个规则都是为了维护一种基本保证:
如果一个对象所提供的性
质少于某个指针允诺的话,该指针就不能指向这个对象。
#include...
classBase
{
public:
Base(){}
virtual~Base(){}
virtualvoidshow(){cout<<"Base";}
};
classDerived:
publicBase
{
public:
Derived():
Base(){}
~Derived(){}
voidprint(){}
voidshow(){cout<<"Derived";}
};
intmain()
{
Basebase;
Derivedderived;
Base*pb=&derived; //Ok,基类指针指向派生类对象;
Derived*pd=&base; //Error,派生类指针指向基类对象;
pb->show(); //Ok,但输出derived;
pb->print(); //Error.不可调用仅派生类有的方法;
typedefvoid(Derived:
:
*Pderived)();
Pderivedpmend=&Base:
:
show; //Ok,基类成员的指针赋值给派生类成员的指针
(derived.*pmend)(); //使用派生类成员指针
typedefvoid(Base:
:
*Pbase)();
Pbasepmenb=&Derived:
:
show; //Error.派生类成员的指针赋值给基类成员的指针
}
reinterpret_cast,C-stylecastorfunction-stylecast
dynamic_cast
classB{/*......*/virtualvoidf();};
classV{/*......*/virtualvoidg();};
classX{/*novirtualfunctions*/};
classD:
publicB,publicvirtualV,publicvirtualX{
//...
};
voidfoo(D&d)
{
B*pb=&d;
D*p1=(D*)pb;//ok,unchecked
D*p2=dynamic_cast(pb);//ok,run-timechecked
V*pv=&d;
D*p3=(D*)pv;//error:
cannotcastfromvirtualbase
D*p4=dynamic_cast(pv);//ok,run-timechecked
X*px=&d;
D*p5=(D*)px;//error:
cannotcastfromvirtualbase
D*p6=dynamic_cast(px);//error:
can'tcastfromnon-polymorphictype
}
1.reinterpret_cast可以转换任意一个32bit整数,包括所有的指针和整数。
可以把任何整数转成指针,也可以把任何指针转成整数,以及把指针转化为任意类型的指针,威力最为强大!
但不能将非32bit的实例转成指针。
总之,只要是32bit的东东,怎么转都行!
2.static_cast和dynamic_cast可以执行指针到指针的转换,或实例本身到实例本身的转换,但不能在实例和指针之间转换。
static_cast只能提供编译时的类型安全,而dynamic_cast可以提供运行时类型安全。
举个例子:
3.class a;class b:
a;class c。
4.上面三个类a是基类,b继承a,c和ab没有关系。
5.有一个函数void function(a&a);
6.现在有一个对象是b的实例b,一个c的实例c。
7.function(static_cast(b)可以通过而function(static(c))不能通过编译,因为在编译的时候编译器已经知道c和a的类型不符,因此static_cast可以保证安全。
8.下面我们骗一下编译器,先把c转成类型a
9.b& ref_b = reinterpret_castc;
10.然后function(static_cast(ref_b))就通过了!
因为从编译器的角度来看,在编译时并不能知道ref_b实际上是c!
11.而function(dynamic_cast(ref_b))编译时也能过,但在运行时就失败了,因为dynamic_cast在运行时检查了ref_b的实际类型,这样怎么也骗不过去了。
12.在应用多态编程时,当我们无法确定传过来的对象的实际类型时使用dynamic_cast,如果能保证对象的实际类型,用static_cast就可以了。
至于reinterpret_cast,我很喜欢,很象c语言那样的暴力转换:
)
13.dynamic_cast:
动态类型转换
14.static_cast:
静态类型转换
15.reinterpret_cast:
重新解释类型转换
16.const_cast:
常量类型转换
17.synamic_cast一般用在父类和子类指针或应用的互相转化;
18.static_cast一般是普通数据类型(如int m=static_cast(3.14));
19.reinterpret_cast很像c的一般类型转换操作
20.const_cast是把cosnt或volatile属性去掉
21.
22..
23.
24.泛型(Generic Types)
25.
26. float f = 12.3;
27. float* pf = &f;
28. // static cast<>
29. // 成功编译, n = 12
30. int n = static_cast(f);
31. // 错误,指向的类型是无关的(译注:
即指针变量pf是float类型,现在要被转换为int类型) //int* pn = static_cast(pf);
32. //成功编译
33. void* pv = static_cast(pf);
34. //成功编译, 但是 *pn2是无意义的内存(rubbish)
35. int* pn2 = static_cast(pv);
36. // reinterpret_cast<>
37. //错误,编译器知道你应该调用static_cast<>
38. //int i = reinterpret_cast(f);
39. //成功编译, 但是 *pn 实际上是无意义的内存,和 *pn2一样
40. int* pi = reinterpret_cast(pf);简而言之,static_cast<> 将尝试转换,举例来说,如float-到-integer,而reinterpret_cast<>简单改变编译器的意图重新考虑那个对象作为另一类型。
41.
42.指针类型(Pointer Types)
43.
44.class CBaseX
45. {
46. public:
47. int x;
48. CBaseX() { x = 10; }
49. void foo() { printf("CBaseX:
:
foo() x=%d/n", x); }
50. };
51. class CBaseY
52. {
53. public:
54. int y;
55. int* py;
56. CBaseY() { y = 20; py = &y; }
57. void bar() { printf("CBaseY:
:
bar() y=%d, *py=%d/n", y, *py);
58. }
59. };
60. class CDerived :
public CBaseX, public CBaseY
61. {
62. public:
63. int z;
64. };情况1:
两个无关的类之间的转换
65.
66.
67. // Convert between CBaseX* and CBaseY*
68. // CBaseX* 和 CBaseY*之间的转换
69. CBaseX* pX = new CBaseX();
70. // Error, types pointed to are unrelated
71. // 错误, 类型指向是无关的
72. // CBaseY* pY1 = static_cast(pX);
73. // Compile OK, but pY2 is not CBaseX
74. // 成功编译, 但是 pY2 不是CBaseX
75. CBaseY* pY2 = reinterpret_cast(pX);
76. // System crash!
!
77. // 系统崩溃!
!
78. // pY2->bar();正如我们在泛型例子中所认识到的,如果你尝试转换一个对象到另一个无关的类static_cast<>将失败,而reinterpret_cast<>就总是成功“欺骗”编译器:
那个对象就是那个无关类。
79.
80.情况2:
转换到相关的类
81.
82. 1. CDerived* pD = new CDerived();
83. 2. printf("CDerived* pD = %x/n", (int)pD);
84. 3.
85. 4. // static_cast<> CDerived* -> CBaseY* -> CDerived*
86. //成功编译,隐式static_cast<>转换
87. 5. CBaseY* pY1 = pD;
88. 6. printf("CBaseY* pY1 = %x/n", (int)pY1);
89. // 成功编译, 现在 pD1 = pD
90. 7. CDerived* pD1 = static_cast(pY1);
91. 8. printf("CDerived* pD1 = %x/n", (int)pD1);
92. 9.
93. 10. // reinterpret_cast
94. // 成功编译, 但是 pY2 不是 CBaseY*
95. 11. CBaseY* pY2 = reinterpret_cast(pD);
96. 12. printf("CBaseY* pY2 = %x/n", (int)pY2);
97. 13.
98. 14. // 无关的 static_cast<>
99. 15. CBaseY* pY3 = new CBaseY();
100. 16. printf("CBaseY* pY3 = %x/n", (int)pY3);
101. // 成功编译,尽管 pY3 只是一个 "新 CBaseY()"
102. 17. CDerived* pD3 = static_cast(pY3);
103. 18. printf("CDerived* pD3 = %x/n", (int)pD3); ---------------------- 输出 ---------------------------
104. CDerived* pD = 392fb8
105. CBaseY* pY1 = 392fbc
106. CDerived* pD1 = 392fb8
107. CBaseY* pY2 = 392fb8
108. CBaseY* pY3 = 390ff0
109. CDerived* pD3 = 390fec
110. 注意:
在将CDerived*用隐式 static_cast<>转换到CBaseY*(第5行)时,结果是(指向)CDerived*(的指针向后) 偏移了4(个字节)(译注:
4为int类型在内存中所占字节数)。
为了知道static_cast<> 实际如何,我们不得不要来看一下CDerived的内存布局。
111.
112.CDerived的内存布局(Memory Layout)
viewplaincopytoclipboardprint?
1.如图所示,CDerived的内存布局包括两个对象,CBaseX 和 CBaseY,编译器也知道这一点。
因此,当你将CDerived* 转换到 CBaseY*时,它给指针添加4个字节,同时当你将CBaseY*转换到CDerived*时,它给指针减去4。
然而,甚至它即便不是一个CDerived你也可以这样做。
2.当然,这个问题只在如果你做了多继承时发生。
在你将CDerived转换 到 CBaseX时static_cast<> 和 reinterpret_cast<>是没有区别的。
3.
4.情况3:
void*之间的向前和向后转换
5.
6.因为任何指针可以被转换到void*,而void*可以被向后转换到任何指针(对于static_cast<> 和 reinterpret_cast<>转换都可以这样做),如果没有小心处理的话错误可能发生。
7.
8.
9.
10. CDerived* pD = new CDerived();
11. printf("CDerived* pD = %x/n", (int)pD);
12. CBaseY* pY = pD; // 成功编译, pY = pD + 4
13. printf("CBaseY* pY = %x/n", (int)pY);
14. void* pV1 = pY; //成功编译, pV1 = pY
15. printf("void* pV1 = %x/n", (int)pV1);
16. // pD2 = pY, 但是我们预期 pD2 = pY - 4
17. CDerived* pD2 = static_cast(pV1);
18. printf("CDerived* pD2 = %x/n", (int)pD2);
19. // 系统崩溃
20. // pD2->bar(); ---------------------- 输出 ---------------------------
21. CDerived* pD = 392fb8
22. CBaseY* pY = 392fbc
23. void* pV1 = 392fbc
24. CDerived* pD2 = 392fbc
25. 一旦我们已经转换指针为void*,我们就不能轻易将其转换回原类。
在上面的例子中,从一个void* 返回CDerived*的唯一方法是将其转换为CBaseY*然后再转换为CDerived*。
26.但是如果我们不能确定它是CBaseY* 还是 CDerived*,这时我们不得不用dynamic_cast<> 或typeid[2]。
27.
28.注释:
29.1. dynamic_cast<>,从另一方面来说,可以防止一个泛型CBaseY* 被转换到CDerived*。
30.2. dynamic_cast<>需要类成为多态,即包括“虚”函数,并因此而不能成为void*。
31.参考:
32.1. [MSDN] C++ Language Reference -- Casting
33.2. Nishant Sivakumar, Casting Basics - Use C++ casts in your VC++.NET programs
34.3. Juan Soulie, C++ Language Tutorial:
Type Casting
35.
36.
37.
使用标准C++的类型转换符:
static_cast、dynamic_cast、reinterpret_cast、和const_cast。
1static_cast
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
不需要有虚函数
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。
这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:
static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
2dynamic_cast
用法:
dynamic_cast(expression)
该运算符把expression转换成type-id类型的对象。
Type-id必须是类的指针、类的引用或者void*;
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
classB{
public:
intm_iNum;
virtualvoidfoo();
};
classD:
publicB{
public:
char*m_szName[100];
};
voidfunc(B*pb){
D*pd1=static_cast(pb);
D*pd2=dynamic_cast(pb);
}
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),
而pd2将是一个空指针。
dynamic_cast使用时要有虚函数,否则会编译出错;static_cast则