我在学习虚拟函数和指针。下面的代码让我思考,既然我们可以以我们想要的方式键入类型的基类指针,为什么需要虚拟函数呢?
class baseclass {
public:
void show() {
cout << "In Base\n";
}
};
class derivedclass1 : public baseclass {
public:
void show() {
cout << "In Derived 1\n";
}
};
class derivedclass2 : public baseclass {
public:
void show() {
cout << "In Derived 2\n";
}
};
int main(void) {
baseclass * bptr[2];
bptr[0] = new derivedclass1;
bptr[1] = new derivedclass2;
((derivedclass1*) bptr)->show();
((derivedclass2*) bptr)->show();
delete bptr[0];
delete bptr[1];
return 0;
}如果在基类中使用虚拟的话,则给出相同的结果。
In Derived 1
In Derived 2我是不是遗漏了什么?
发布于 2014-03-16 13:15:56
您的示例似乎有效,因为没有数据,没有虚拟方法,也没有多重继承。尝试将int value;添加到derivedclass1,将const char *cstr;添加到derivedclass2,在相应的构造函数中初始化这些构造函数,并将它们添加到相应的show()方法中。
您将看到show()将如何打印垃圾值(如果您将指针转换为derivedclass1而不是)或崩溃(如果您在类实际上不是该类型时将指针转换为derivedclass2 ),或者行为不正常。
C++类成员函数AKA方法只不过是函数,它有一个隐藏的额外参数,this指针,它们假设它指向一个类型正确的对象。因此,当您有一个derivedclass1类型的对象,但是您将指向它的指针转换为derivedclass2类型时,那么如果没有虚拟方法,则会发生这样的情况:
derivedclass2的方法,因为您明确表示“这是指向derivedclass2的指针”。this的指针。它认为它指向了derivedclass2的实际实例,该实例将在某些偏移量处具有特定的数据成员。derivedclass1,则该内存包含一些完全不同的内容。因此,如果方法认为有一个char指针,但实际上没有,那么访问它所指向的数据可能会访问非法地址和崩溃。如果您使用的是虚拟方法,并且有指向公共基类的指针,那么当您调用一个方法时,编译器会生成代码来调用正确的方法。它实际上插入了代码和数据(使用一个充满虚拟方法指针的表,通常称为vtable,每个类一个,以及指向它的指针,每个对象/实例一个),它知道如何调用正确的方法。因此,当您调用一个虚拟方法时,它不是直接调用,而是对象具有指向真实类的vtable的额外指针,它指示应该为该对象真正调用哪种方法。
总之,类型转换绝不是虚拟方法的替代方法。另外,每一种类型的铸造都是问“为什么在这里铸造?这个软件是否存在一些根本问题,如果它需要在这里铸造?”的地方。类型转换的合法用例非常罕见,尤其是对于OOP对象。此外,不要使用带有对象指针的C样式类型强制转换,如果确实需要转换,则使用static_cast和dynamic_cast。
发布于 2014-03-16 13:11:09
如果使用虚拟函数,则调用函数的代码不需要知道对象的实际类。您只需盲目地调用该函数,就会执行正确的函数。这是多态性的基础。
类型转换总是有风险的,并且会在大型程序中导致运行时错误。
您的代码应该是开放供扩展,但对修改关闭。
希望这能有所帮助。
发布于 2014-03-16 13:38:13
在您的直到运行时才知道派生类型(例如,当它依赖于用户输入时),您需要虚拟函数。
在您的示例中,您对derivedclass2和derivedclass1进行了硬编码的转换。现在你会在这里做什么?
void f(baseclass * bptr)
{
// call the right show() function
}也许您的困惑源于这样一个事实:您还没有遇到虚拟函数实际上是有用的情况。当您总是在编译时准确地知道您正在操作的具体类型时,那么您根本不需要虚拟函数。
示例代码中还有另外两个问题:
dynamic_cast (当然,当您使用虚拟函数来解决设计中的问题时,通常不需要强制转换)。https://stackoverflow.com/questions/22437189
复制相似问题