我正在研究虚函数和指针。下面的代码让我思考,当我们可以按照我们想要的方式键入转换基类指针时,为什么需要虚函数?
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;
}
如果我们在基类中使用virtual,则会得到相同的结果。
In Derived 1
In Derived 2
我错过了什么吗?
答案 0 :(得分:2)
如果使用虚函数,则调用函数的代码不需要知道对象的实际类。你只需要盲目调用函数,就会执行正确的函数。这是多态性的基础。
类型转换总是有风险的,并且可能导致大型程序中的运行时错误。
您的代码应为open for extension but closed for modifications。
希望这有帮助。
答案 1 :(得分:2)
您的示例似乎有效,因为没有数据,没有虚方法,也没有多重继承。尝试将int value;
添加到derivedclass1
,将const char *cstr;
添加到derivedclass2
,在相应的构造函数中初始化这些内容,然后将这些内容添加到相应的show()
方法中。
您将看到show()
将如何打印垃圾值(如果您将指针转换为derivedclass1
则不显示)或崩溃(如果您实际上将类指针转移到derivedclass2
不属于那种类型),或表现得很奇怪。
C ++类成员函数AKA方法只不过是函数,它们带有一个隐藏的额外参数this
指针,并且它们假定它指向一个正确类型的对象。所以当你有一个derivedclass1
类型的对象,但是你把它指向它时要输入derivedclass2
,那么没有虚方法会发生这样的事情:
derivedclass2
的方法被调用,因为你明确地说“这是指向derivedclass2
”的指针。this
的指针。它认为它指向derivedclass2
的实际实例,它在某些偏移处会有某些数据成员。derivedclass1
,则该内存包含完全不同的内容。因此,如果方法认为有一个字符指针,但事实上没有,那么访问它指向的数据可能会访问非法地址并崩溃。如果您改为使用虚方法,并且具有指向公共基类的指针,那么当您调用方法时,编译器会生成用于调用正确方法的代码。它实际上插入代码和数据(使用填充了虚拟方法指针的表,通常称为 vtable ,每个类一个,指向它的指针,每个对象/实例一个),它知道调用右边的方法。因此,当您调用虚方法时,它不是直接调用,而是该对象具有指向真实类的 vtable 的额外指针,它指示应该使用哪种方法被称为该对象。
总之,类型转换绝不是虚拟方法的替代方法。并且,作为旁注,每个类型的演员阵容都是一个问题“为什么会在这里演出?这个软件是否存在根本问题,如果它需要演员?”。类型转换的合法用例确实非常罕见,特别是对于OOP对象。此外,永远不要使用带有对象指针的C风格类型转换,如果你真的需要施放,请使用static_cast
和dynamic_cast
。
答案 2 :(得分:1)
您需要虚拟功能,以便在运行时之前不知道派生类型(例如,当它取决于用户输入时)。
在您的示例中,您对derivedclass2
和derivedclass1
进行了硬编码转换。那你在这做什么?
void f(baseclass * bptr)
{
// call the right show() function
}
也许您的困惑源于您尚未遇到虚拟功能实际有用的情况。当你总是在编译时准确地知道你正在操作的具体类型时,你就根本不需要虚函数。
示例代码中的其他两个问题:
dynamic_cast
(当然,当你使用虚拟函数解决它们旨在解决的问题时,你通常不需要进行投射)。 / LI>