为啥 C++ 不能用“超类”类型的右值初始化“派生类”类型的变量?
Posted
技术标签:
【中文标题】为啥 C++ 不能用“超类”类型的右值初始化“派生类”类型的变量?【英文标题】:Why C++ cannot initialize a variable of type "derived class" with an rvalue of type "super class"?为什么 C++ 不能用“超类”类型的右值初始化“派生类”类型的变量? 【发布时间】:2011-07-15 14:28:19 【问题描述】:请考虑以下代码:
class a
int a1;
public:
a()
printf("foo1\n");
;
class b : public a
int a2;
public:
b()
printf("foo2\n");
;
int main (int argc, const char * argv[])
b *instance = new a();
return 0;
它给出错误:无法使用“a*”类型的右值初始化“b*”类型的变量 我写的时候效果很好
a *instance = new b();
输出是:
foo1
foo2
谁能解释一下原因?我将非常感激:)
另一件事,如果我写了
instance->~a();
在return 0;
之上没有任何额外的事情发生。这是因为构造函数只能调用一次吗?
【问题讨论】:
~a 是析构函数,而不是构造函数。直接调用其中任何一个都很奇怪。 语句instance->~a();
调用析构函数,而不是构造函数。您通常不需要手动执行此操作。对于使用new
分配的对象,请使用delete
运算符,如delete instance;
。
我只是在学习。我想澄清一些疑问。对不起,如果我伤害了你。感谢您的回答。
我可以看到我的问题的 -1s。我认为我冒犯了社区。再次抱歉,感谢您的回答。
你不应该显式调用析构函数。唯一需要这样做的情况是对象是使用placement new 运算符构造的。
【参考方案1】:
让我们更具体一点:
class Animal
int a1;
public:
Animal()
printf("Animal\n");
;
class Bat : public Animal
int a2;
public:
Bat()
printf("bat\n");
;
int main (int argc, const char * argv[])
Bat *instance = new Animal();
return 0;
您现在明白为什么这可能无效了吗?您使用new Animal()
创建的内容可以是任何类型的动物。将其分配给Bat
的变量可能无效,因为它可能不是蝙蝠。
【讨论】:
【参考方案2】:b
是 a
。a
不是 b
。
您可以将Giraffe
类型的实例放入Animal
类型的变量中。
但是,您不能将Animal
的实例放入Giraffe
类型的变量中(如果它是Porcupine
怎么办?)
【讨论】:
【参考方案3】:根据定义,派生类是可以做基类可以做的超集的东西。派生类可以做基类可以做的所有事情,但反之则不行。因此,将派生类视为基类是有意义的,反之则不然。例如:
class Animal
void eat();
;
class Dog : public Animal
void bark();
将Dog
视为通用Animal
是完全合理的,但如果将通用Animal
订购到bark
,则没有合理的做法。
【讨论】:
感谢@dssimcha 的解释【参考方案4】:由于a
不是b
,您不能将指向b
的指针分配给a
类型的对象。
由于b
是a
,因此它确实可以正常工作。
【讨论】:
【参考方案5】:b
是a
的一种。但是,a
不是b
的类型。如果b
包含其他成员,那么如果b
确实引用了a
类型的对象,那么当您尝试访问它们时会发生什么。
这不是一个安全的演员表。
【讨论】:
【参考方案6】:请始终记住这一点,您不能将基类对象分配给派生类指针。 [派生类对象]是[基类对象]。 反之则不然。
【讨论】:
【参考方案7】:问题在于“是”关系。 class a
的对象不是 class b
的对象所以
b *instance = new a(); // won't work
意味着您尝试将指向class b
的指针设置到不是对象class b
的对象上。同时你可以做相反的事情:
a* instance = new b(); //will work
因为class b
的对象也是class a
的对象,所以在这里您可以将指向class a
的指针设置到确实是class a
的对象上。
【讨论】:
【参考方案8】:关于析构函数 -
如果操作数的静态类型与动态类型不同,则静态类型表现为基类,其析构函数必须是虚拟的。
此外,派生类的指针/引用与基类的指针/引用类型兼容。但其他情况并非如此。 (无传递性)
在你的情况下 -
a *instance = new b(); // This should be correct way.
instance
的静态类型为a*
,而动态类型为b*
。所以,a
作为一个基类,它的析构函数必须是virtual。
【讨论】:
【参考方案9】:您不能将a
替换为b
,因为a
不具备b
的所有功能。因此,进行这样的分配是没有意义的。
对于第二部分,99% 的时间你说 delete var
来销毁分配给 new
的东西,而不是显式调用析构函数,这是一个高级主题。
【讨论】:
以上是关于为啥 C++ 不能用“超类”类型的右值初始化“派生类”类型的变量?的主要内容,如果未能解决你的问题,请参考以下文章
为啥定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值?
无法使用“const Node *”类型的右值初始化“Node *”类型的变量
从“A”类型的右值初始化“A&”类型的非常量引用无效[重复]
无法使用“value_type *”类型的右值(又名 char*)初始化“const unsigned char *”类型的参数