【问题标题】:Virtual function calling using dereference object使用取消引用对象调用虚函数
【发布时间】:2010-06-21 05:24:37
【问题描述】:

我有一个指向派生类对象的基类指针。我在下面的代码中使用两种不同的方式调用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;
    }

【问题讨论】:

    标签: c++ inheritance


    【解决方案1】:
    // SCENARIO 1
    (*obj).foo();
    

    注意

    1. obj 在这里用词不当,因为它不是指一个对象,而是指一个指针
    2. (*ptr).foo() 只是实现ptr-&gt;foo() 的一种迂回方式。

    *ptr 不会生成对象,而是生成对象的引用 Base&amp;。而通过 reference 调用的虚函数是动态调度的,就像通过指针调用一样。

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        如果您稍微考虑一下实现,它会有所帮助。在第二种情况下,您实际上是在创建一个 Base 类型的新对象,该对象将附带一个新的虚函数表。但在第一种情况下,*obj 将“指向”或者更确切地说是引用一个对象,该对象仍然具有 Derived 类型的对象的虚函数表。

        【讨论】:

          【解决方案4】:

          作为其他答案的补充。

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

          这是维基百科条目:

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

          还有一个关于对象切片的stackoverflow的问题:

          What is object slicing?

          【讨论】:

            【解决方案5】:

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

            在第二种情况下,Base 类的新对象正在通过复制构造函数进行实例化,由于它是 Base 类对象,它将调用 Base 类版本的 foo()

            【讨论】:

              【解决方案6】:

              多态适用于引用(解引用指针的结果),就像它适用于指针一样。

              【讨论】:

                【解决方案7】:

                “因为它已经被取消引用”是什么意思?

                基类指针 obj 指向派生类对象,由于您已将函数 foo() 声明为 virtual,派生类 foo() 将被调用。

                【讨论】:

                  【解决方案8】:

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

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

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

                  在第二种情况下,obj1 的动态类型为Base,因此调用了Base::foo

                  换句话说,一切都按预期进行。这让人很奇怪,让你问你的问题。是什么让你期待不同的东西?

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-09-23
                    • 2013-05-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多