首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当基类指针转换给出相同的结果时,为什么使用虚拟函数?

当基类指针转换给出相同的结果时,为什么使用虚拟函数?
EN

Stack Overflow用户
提问于 2014-03-16 13:03:03
回答 3查看 1.6K关注 0票数 0

我在学习虚拟函数和指针。下面的代码让我思考,既然我们可以以我们想要的方式键入类型的基类指针,为什么需要虚拟函数呢?

代码语言:javascript
复制
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;
}

如果在基类中使用虚拟的话,则给出相同的结果。

代码语言:javascript
复制
In Derived 1
In Derived 2

我是不是遗漏了什么?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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_castdynamic_cast

票数 3
EN

Stack Overflow用户

发布于 2014-03-16 13:11:09

如果使用虚拟函数,则调用函数的代码不需要知道对象的实际类。您只需盲目地调用该函数,就会执行正确的函数。这是多态性的基础。

类型转换总是有风险的,并且会在大型程序中导致运行时错误。

您的代码应该是开放供扩展,但对修改关闭

希望这能有所帮助。

票数 2
EN

Stack Overflow用户

发布于 2014-03-16 13:38:13

在您的直到运行时才知道派生类型(例如,当它依赖于用户输入时),您需要虚拟函数。

在您的示例中,您对derivedclass2derivedclass1进行了硬编码的转换。现在你会在这里做什么?

代码语言:javascript
复制
void f(baseclass * bptr)
{
    // call the right show() function
}

也许您的困惑源于这样一个事实:您还没有遇到虚拟函数实际上是有用的情况。当您总是在编译时准确地知道您正在操作的具体类型时,那么您根本不需要虚拟函数。

示例代码中还有另外两个问题:

  1. 使用C样式的强制转换而不是C++样式的dynamic_cast (当然,当您使用虚拟函数来解决设计中的问题时,通常不需要强制转换)。
  2. 多形性地处理阵列。请参阅Scott的更有效的C++书中的第3项(“永远不要多形性地对待数组”)。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22437189

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档