Laz*_*zer 7 c++ inheritance pointers
我在这里和这里看到了很少有关于这类基本问题的宠物和狗类型的例子,但它们对我来说没有意义,这就是为什么.
假设我们有以下类结构
class Pet {};
class Dog : public Pet {};
Run Code Online (Sandbox Code Playgroud)
然后是以下声明
a (Dog) is a (Pet)
在我的观点中,在现实生活中可能是真的,但在C++中却不是这样.只需看看Dog对象的逻辑表示,它看起来像这样:
更合适的说法
a (Dog) has a (Pet)
要么
a (Pet) is a subset of (Dog)
如果你注意到它与"狗是宠物"的逻辑相反
现在的问题是,#2不允许下面的#1:
Pet* p = new Dog; // [1] - allowed!
Dog* d = new Pet; // [2] - not allowed without explicit casting!
Run Code Online (Sandbox Code Playgroud)
我的理解是不[1]
应该在没有警告的情况下被允许,因为指针不应该能够指向其超集类型的对象(Dog对象是Pet的超集),因为Pet对新成员一无所知狗可能已经宣布(上图中的狗 - 宠物子集).
[1]
相当于int*
试图指向一个double
对象!
很明显,我错过了一个关键点,这将使我的整个推理颠倒过来.你能告诉我它是什么吗?
我相信与现实世界的例子相似只会使事情复杂化.我希望从技术细节方面理解这一点.谢谢!
Irf*_*rfy 30
编辑:重新阅读您的问题,我的回答让我在顶部说出这个:
您对is a
C++(多态,一般)的理解是错误的.
A is B
意思是A has at least the properties of B, possibly more
,根据定义.
这与你的Dog
a Pet
和[的属性]是[属性]的子集的陈述兼容Dog
.
这是多态性和继承的定义问题.您绘制的图表与实例的内存中表示对齐Pet
和Dog
,但在您解释它们的方式是误导.
Pet* p = new Dog;
Run Code Online (Sandbox Code Playgroud)
指针p
被定义为指向任何与Pet兼容的对象,在C++中,它是任何子类型Pet
(注意:Pet
根据定义,它是自身的子类型).确保运行时,当p
访问后面的对象时,它将包含Pet
预期包含的任何内容,甚至可能包含更多内容."可能更多"部分是Dog
您的图表中的部分.绘制图表的方式会带来误导性的解释.
想想内存中特定于类的成员的布局:
Pet: [pet data]
Dog: [pet data][dog data]
Cat: [pet data][cat data]
Run Code Online (Sandbox Code Playgroud)
现在,无论何时Pet *p
指向,都需要有[pet data]
零件,还可以选择其他任何东西.从上面的列表中,Pet *p
可能指向三者中的任何一个.只要您使用Pet *p
访问对象,您只能访问[pet data]
,因为您不知道之后是什么,如果有的话.这是一份合同,说这至少是宠物,也许更多.
无论Dog *d
指向什么,都必须拥有[pet data]
和[dog data]
.所以记忆中唯一可能指向的对象就是狗.相反,通过Dog *d
,您可以访问[pet data]
和[dog data]
.类似的Cat
.
让我们解释你感到困惑的声明:
Run Code Online (Sandbox Code Playgroud)Pet* p = new Dog; // [1] - allowed! Dog* d = new Pet; // [2] - not allowed without explicit casting!
我的理解是不应该在没有警告的情况下允许1,因为指针不能指向其超集类型的对象(Dog对象是Pet的超集),因为Pet对新成员一无所知狗可能已经宣布(上图中的狗 - 宠物子集).
指针p
期望[pet data]
在它指向的位置找到.由于右手边是a Dog
,并且每个Dog
物体都[pet data]
在它的前面,所以[dog data]
指向一个类型的物体Dog
是完全可以的.
编译器不知道什么别的是指针的后面,这就是为什么你不能访问[dog data]
通过p
.
允许声明是因为[pet data]
编译器在编译时可以保证存在.(这个陈述显然是从现实中简化,以适合您的问题描述)
1相当于一个int*试图指向一个双重对象!
int和double之间没有这样的子类型关系,就像C++之间Dog
和Pet
C++一样.尽量不要将这些混合到讨论中,因为它们是不同的:你在int和double的值之间转换((int) double
是显式的,(double) int
是隐式的),你不能在指向它们的指针之间进行转换.忘了这个比较.
至于[2]:在声明指出" d
指向具有一个对象[pet data]
和[dog data]
,可能更多." 但是你只是分配[pet data]
,所以编译器告诉你不能这样做.
实际上,编译器无法保证这是否正常并且拒绝编译.有合理的情况,编译器拒绝编译,但程序员,你知道更好.这就是static_cast
和dynamic_cast
是.我们上下文中最简单的例子是:
d = p; // won't compile
d = static_cast<Dog *>(p); // [3]
d = dynamic_cast<Dog *>(p); // [4]
Run Code Online (Sandbox Code Playgroud)
[3]将永远成功,并导致可能难以跟踪的错误,如果p
不是真的Dog
.如果不是真的,
[4]将会返回.NULL
p
Dog
我热烈建议尝试这些演员,看看你得到了什么.假设RTTI已启用,您应该[dog data]
从static_cast
和NULL
指针获取垃圾.dynamic_cast