使用解除引用对象调用虚函数

时间:2010-06-21 05:24:37

标签: c++ inheritance

我有一个指向派生类对象的基类指针。我在下面的代码中使用两种不同的方式调用foo()函数。为什么Derived::foo()在第一种情况下被调用?不应(*obj).foo()调用Base::foo()函数,因为它已被解除引用?

    class Base
    {
    public:
        Base() {}
        virtual void foo() { std::cout << "Base::foo() called" << std::endl; }
        virtual ~Base() {};
    };

    class Derived: public Base
    {
    public:
        Derived() : Base() {}
        virtual void foo() {  std::cout << "Derived::foo() called" << std::endl; }
        virtual ~Derived() {};
    };

    int main() {
        Base* obj = new Derived();
   // SCENARIO 1
        (*obj).foo();
// SCENARIO 2
        Base obj1 = *obj;
        obj1.foo();

        return 0;
    }

8 个答案:

答案 0 :(得分:16)

// SCENARIO 1
(*obj).foo();

请注意

  1. obj在这里用词不当,因为它不是指对象,而是指指针
  2. (*ptr).foo()只是一种迂回的方式ptr->foo()
  3. *ptr不会生成对象,但会在 引用 Base&中生成对象。通过引用进行的虚函数调用受动态调度的影响,就像通过指针调用一样。

    // SCENARIO 2
    Base obj1 = *ptr;
    obj1.foo();
    

    你在这里做的是通过slicing创建一个全新的对象:它只有*ptr的基类部分。你想要的是这个:

    Base& ref = *ptr;
    ref.foo();
    

答案 1 :(得分:3)

场景2创建一个Base类型的全新对象。因此,当我们执行obj1.foo()时,对象根本不是派生的;我们无法调用派生函数。

然而,在场景1中,对象实际上是Derived的一个实例,我们通过Base指针访问它。这正是虚拟功能所针对的情况;使用派生类的实现。

答案 2 :(得分:1)

如果您对实现有所了解,这会有所帮助。在第二个场景中,您实际上是在创建一个Base类型的新对象,它将带有一个新的虚函数表。但是在第一个场景中*obj将“指向”,或者更确切地说是引用一个仍然具有Derived类型对象的虚函数表的对象。

答案 3 :(得分:1)

作为其他答案的补充。

对于场景2中发生的事情的技术术语是对象切片。

这是维基百科条目:

http://en.wikipedia.org/wiki/Object_slicing

这是关于对象切片的stackoverflow的另一个问题:

What is object slicing?

答案 4 :(得分:1)

在第一种情况下,由于上面解释的明显原因,将调用foo()的派生版本。除了其他答案,*(*Obj).func()**Obj->func()*同义。

在第二种情况下,通过复制构造函数实例化类Base的新对象,因为它是Base类对象,它将调用Basefoo()类}。

答案 5 :(得分:0)

多态性对引用(取消引用指针的结果)起作用,就像在指针上一样。

答案 6 :(得分:0)

你是什么意思“因为它已被解除引用”?

基类指针obj指向派生类对象,因为你声明了函数foo()virtual,所以将调用派生类foo()。

答案 7 :(得分:0)

(这是一个相当奇怪的问题。我宁愿有人问为什么在第二种情况下Derived::foo没有被调用。)

在C ++语言中,调用哪个版本的虚函数完全独立于已经“取消引用”的内容和内容。解除引用没有任何区别。唯一重要的是调用中使用的对象的动态类型

在第一种情况下调用Derived::foo,因为*obj对象的动态类型为Derived

在第二种情况下,动态类型obj1Base,因此调用Base::foo

换句话说,一切都按预期工作。这让一个奇迹让你问你的问题。是什么让你期待不同的东西?