【问题标题】:Dynamic binding in C++ on copied object复制对象上的 C++ 中的动态绑定
【发布时间】:2012-07-30 08:47:49
【问题描述】:

我的虚函数有问题: 以下是一些代码作为示例:

class A
   {
      public : virtual  void print(void)
           {
              cout<< "A::print()"<<endl;
           }
    };
 class B : public A
    {
      public : virtual void print(void)
           {
               cout<<"B::print()"<<endl;
           }
    };
 class C : public A
    {
      public : void print(void)
            {
               cout<<"C::print()"<<endl;
            }
     };
  int main(void)
     {
         A a,*pa,*pb,*pc;
         B b;
         C c;
         pa=&a;
         pb=&b;
         pc=&c;

         pa->print();
         pb->print();
         pc->print();

         a=b;
         a.print();
         return 0;
       }

结果: 打印() B::打印() C::打印() A::print()

我知道它是一个多态,并且知道有一个叫做虚函数表的表,但我不知道它是如何实现的,并且

   a=b;
   a.print();

结果是:A::print() 不是 B::print(),为什么它没有多态性。 谢谢!

【问题讨论】:

    标签: c++ virtual-functions dynamic-binding


    【解决方案1】:

    对象a 仍然是类型 A。该分配仅从b 复制数据,它不会使a 成为B 对象。

    这叫object slicing

    【讨论】:

      【解决方案2】:
      a=b;
      a.print();
      

      它将打印A::print(),因为a=b 会导致对象切片,这意味着a 仅获得ba-subobject。阅读本文:

      请注意,运行时多态性只能通过指针引用类型来实现。在上面的代码中,a既不是pointer,也不是reference类型:

      A * ptr = &b; //syntax : * on LHS, & on RHS
      A & ref =  b; //syntax : & on LHS, that is it!
      
      ptr->print(); //will call B::print() (which you've already seen)
      ref.print();  //will call B::print() (which you've not seen yet)
      

      【讨论】:

        【解决方案3】:

        因为a 不是指针。它是A 的一个实例,a=b; 的赋值复制b 的实例到a。但函数调用是在 A 的一个实例上。

        【讨论】:

          【解决方案4】:

          当您执行a = b; 时,您的b 对象被切片,即仅复制其中的A 部分。多态性仅通过指针和引用起作用。搜索“对象切片”了解主题。

          【讨论】:

            【解决方案5】:

            要了解有关虚拟方法表的更多信息,请参阅wiki。但在一般情况下,该表保留了信息方法的地址。因此,表中的 A 类将有一条记录表明方法 print 在地址 X 中。当您执行 pa=&b 时,B 类只需将表替换为其中的一个这样 print 方法的地址将指向地址 Y。

            但是,当您执行 a=b 时,您会复制对象。在这种情况下,多态性不起作用。

            【讨论】:

              【解决方案6】:

              A 类型的对象上调用any 成员函数后,您仍然拥有A 对象(除了显式的析构函数调用,它什么都没有留下)。

               a = b;
              

              类实例的分配只是对名为“operator=”的特定成员函数的调用。这里的“operator=”没有什么特别之处,只是它的名字是标准的。您可以使用其他名称进行分配:

              a = b;
              // you could as well write:
              a.assign(b);
              // (if such member was defined)
              

              就像你可以写成 add(a,b) 而不是 a+b,但 a+b 更具可读性。

              对函数的显式调用永远不会改变调用它的变量的类型:

              A a;
              foo(a);
              // a is still a A
              a.bar();
              // a is still a A
              

              a 的声明类型为A,不能更改为其他类型:这是a 的不变量。

              对于指针也是如此:指向A 的类型指针的变量将始终具有指向A 的类型指针

              void foo (A*&);
              A *bar();
              
              A a;
              A *p = &a;
              foo (p); // might change p
              // a still has type: pointer to A
              p = bar();
              // a still has type: pointer to A
              

              p 可能指向B 类型的对象,因此,在运行时,*p 的动态类型将是B;但p 的动态类型始终为A*

              【讨论】:

                猜你喜欢
                • 2020-10-29
                • 2012-02-26
                • 2011-02-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多