当使用指针来使用具有多态性的函数时,指针必须是基类的吗?
When using a pointer to use functions with polymorphism, does the pointer have to be of the base class?
当我编译并运行这段代码时:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void doSomething()
{
cout << endl << "hi this is base";
}
};
class DerivedA : public Base
{
public:
void doSomething()
{
cout << endl << "hi this is derivedA";
}
};
class DerivedB : public Base
{
public:
void doSomething(){
cout << endl << "hi this is derivedB";
}
};
int main () {
DerivedB bb;
Base* cc;
cc = (DerivedA*) &bb;
cc->doSomething();
}
输出为hi this is derivedB"
。
现在,这只是一个情况下,我做了一些严格非法的,我的编译器设法编译它?因为我可以想象,DerivedA
指针在调用doSomething
时不知道它指向DerivedB
对象,因为那里没有基类/派生类关系。
有没有比http://www.cplusplus.com/doc/tutorial/的教程更深入地解释c++继承和多态性背后的复杂性的资源?
cc = (DerivedA*) &bb;
叮叮叮!这里有未定义的行为!
这是不是我做了一些违法的事情
是的,完全是非法的。或者,不完全是非法的,但是未定义的行为,这几乎是非法的。
回答问题标题:不,你不需要基类指针。然而,你不能只是在不相关的类型之间强制转换,并希望它能工作。
DeriveA是Base而DeriveB是Base这两种类型都是从基类派生出来的你可以使用基指针来调用DeriveA或DeriveB(多态性)但是你不能从一个子类型转到另一个
正如Xeo指出的那样,您的代码是可编译的,但具有未定义的行为。您应该使用c++的显式强制转换操作符——static_cast
、dynamic_cast
、reinterpret_cast
和const_cast
——来防止此类错误。
查看有关dynamic_cast
的部分:http://www.cplusplus.com/doc/tutorial/typecasting/
cc = (DerivedA*) &bb;
因为DerivedA
和DerivedB
没有直接的继承路径,所以不能使用static_cast
从DerivedB*
转换到DerivedA*
。这意味着C风格强制转换相当于reinterpret_cast
。
虽然可以保证如果将此reinterpret_cast
的结果强制转换回其原始类型,它将产生相同的指针值,但对于中间指针的值没有任何保证,结果是未指定的。这意味着(DerivedA*)&bb
可能根本不指向一个有效的对象,并且将该指针转换为Base*
(通过将其赋值给cc
)的结果可能不指向一个有效的Base
类。
由于不能保证cc
的值,通过它调用成员函数可能会导致未定义行为
将指向DerivedB
对象的指针转换为DerivedA
对象,然后像您这样调用DerivedA
的一个函数是自找麻烦(本质上是未定义的行为),因为DerivedB
不是从类型DerivedA
派生的。
就编译器而言,这并不是严格意义上的"非法",但你不应该这样做,如果你想从你的代码中获得可靠的行为,这不是你想要做的事情。
如果你想在运行时做额外的检查,你应该检查dynamic_cast
,而不是使用c风格的强制转换。
为什么它可能工作与虚值表的布局方式有关。在g++中,如果使用命令-fdump-class-hierarchy
进行编译,将得到虚变量表的转储,如下所示:
Vtable for DerivedA
DerivedA::_ZTV8DerivedA: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI8DerivedA)
16 (int (*)(...))DerivedA::doSomething
...
Vtable for DerivedB
DerivedB::_ZTV8DerivedB: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI8DerivedB)
16 (int (*)(...))DerivedB::doSomething
可以看到,对于类型DerivedA
和DerivedB
的两个实例,函数doSomething
处于相同的偏移量。在这种情况下,指针指向DerivedB::doSomething
,这就是为什么输出是"hi this is derivedB"
。
- 如何使用基类指针引用派生类成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如果基类包含双指针成员,则派生类的构造函数
- 使用基类指针调用基类的值构造函数的语法是什么?
- 如何在基类指针向量的元素上应用重载的多态函数
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 指向基类派生类的 std::unique_ptr 的指针
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 如何在成为指向基类的指针后保留对子类方法的使用?
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- std::variant vs指向C++中异构容器基类的指针
- 模板化函数或具有指向基类的指针的函数
- 指向基类的唯一指针
- C++函数解析基类指针到派生类指针
- 指向基类的指针的 std::vector 的深层副本
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 无法从指针派生类转换为指针基类(多态性)
- 使用指针基类的=操作符的多态性