C++ 什么是对象切片?

C++ 什么是对象切片?,c++,inheritance,c++-faq,object-slicing,C++,Inheritance,C++ Faq,Object Slicing,有人在IRC中提到它是切片问题。谷歌中第三个匹配的“C++切片”给了我这篇维基百科文章和这篇文章(很热,但前几篇文章定义了这个问题): 所以当你把一个子类的一个对象分配给超类时。超类对子类中的附加信息一无所知,并且没有空间存储它,因此附加信息被“切掉” 如果这些链接没有提供足够的信息来回答“好的答案”,请编辑您的问题,让我们知道您还需要什么。“切片”是指您将派生类的对象分配给基类的实例,从而丢失部分信息-其中一些信息被“切片”掉 比如说, class A { int foo; }; cl

有人在IRC中提到它是切片问题。

谷歌中第三个匹配的“C++切片”给了我这篇维基百科文章和这篇文章(很热,但前几篇文章定义了这个问题):

所以当你把一个子类的一个对象分配给超类时。超类对子类中的附加信息一无所知,并且没有空间存储它,因此附加信息被“切掉”

如果这些链接没有提供足够的信息来回答“好的答案”,请编辑您的问题,让我们知道您还需要什么。

“切片”是指您将派生类的对象分配给基类的实例,从而丢失部分信息-其中一些信息被“切片”掉

比如说,

class A {
   int foo;
};

class B : public A {
   int bar;
};
因此,
B
类型的对象有两个数据成员,
foo
bar

那么如果你要写这个:

B b;

A a = b;

然后
b
中关于成员
bar
的信息将丢失在
a

中。如果您有基类
a
和派生类
b
,则可以执行以下操作

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);
现在,方法
wantAnA
需要一份
derived
的副本。但是,派生的对象不能完全复制,因为类
B
可以创建不在其基类
A
中的其他成员变量

因此,要调用
wantAnA
,编译器将“切掉”派生类的所有其他成员。结果可能是您不想创建的对象,因为

  • 它可能是不完整的
  • 它的行为类似于
    A
    -对象(类
    B
    的所有特殊行为都将丢失)

    • 切片问题很严重,因为它会导致内存损坏,并且很难保证程序不会受到损坏。要在语言之外进行设计,支持继承的类应该只能通过引用(而不是通过值)进行访问。D编程语言具有这个特性


      考虑从A派生的类A和类B。如果A部件具有指针p,并且B实例将p指向B的附加数据,则可能发生内存损坏。然后,当附加数据被切掉时,p指向垃圾。

      。。。为什么丢失派生信息是不好的。。。因为派生类的作者可能已经更改了表示形式,因此切掉额外信息会更改对象表示的值。如果派生类用于缓存对某些操作更有效但转换回基表示代价高昂的表示,则可能发生这种情况

      我还认为有人应该提到你应该做些什么来避免切片。。。 获取C++编码标准的副本,101条规则指南和最佳实践。处理切片是#54

      它建议使用一种稍微复杂的模式来完全处理这个问题:拥有一个受保护的复制构造函数、一个受保护的纯虚拟DoClone和一个带有断言的公共克隆,该断言将告诉您(进一步)派生类是否未能正确实现DoClone。(克隆方法生成多态对象的正确深度副本。)


      也可以在基础显式上标记复制构造函数,如果需要的话,可以显式切片。

      < P> C++中的切片问题源于其对象的值语义,这主要是由于与C结构的兼容性。您需要使用显式引用或指针语法来实现大多数其他执行对象的语言中的“正常”对象行为,即对象总是通过引用传递


      简短的回答是,通过按值将派生对象指定给基础对象,即剩余对象只是派生对象的一部分,来切片对象。为了保留值语义,切片是一种合理的行为,其用途相对较少,这在大多数其他语言中是不存在的。有些人认为它是C++的一个特性,而很多人认为它是C++的一个怪癖/错误特征。

      在我看来,切片不是一个问题,而不是当你自己的类和程序的架构设计不好时。 如果我将一个子类对象作为参数传递给一个方法,该方法接受一个超类类型的参数,我当然应该知道这一点,并且在内部知道,被调用的方法将只处理超类(aka baseclass)对象


      在我看来,只有一种不合理的期望,即在请求基类的地方提供子类会以某种方式导致特定于子类的结果,这会导致切片成为一个问题。它要么在方法的使用上设计糟糕,要么子类实现糟糕。我猜这通常是因为牺牲了良好的OOP设计,取而代之的是方便性或性能提升。

      1。切片问题的定义

      如果D是基类B的派生类,则可以将派生类型的对象指定给base类型的变量(或参数)

      范例

      class宠物
      {
      公众:
      字符串名;
      };
      狗类:公共宠物
      {
      公众:
      弦品种;
      };
      int main()
      {   
      狗;
      宠物;
      dog.name=“汤米”;
      dog.bride=“袋鼠犬”;
      宠物=狗;
      cout name=“汤米”;
      ptrD->繁殖=“袋鼠狗”;
      ptrP=ptrD;
      不能繁殖;
      
      在这种情况下,动态变量的所有数据成员或成员函数
      ptrD(后代类对象)指向的对象将丢失。此外,如果需要使用函数,该函数必须是一个虚拟函数。

      好的,在阅读了许多解释对象切片的文章后,我将尝试使用它,但不会解释它是如何成问题的

      可能导致内存损坏的恶性情况如下:

      • 类在多态基类上提供(意外地,可能是编译器生成的)赋值
        class Pet
        {
         public:
            string name;
        };
        class Dog : public Pet
        {
        public:
            string breed;
        };
        
        int main()
        {   
            Dog dog;
            Pet pet;
        
            dog.name = "Tommy";
            dog.breed = "Kangal Dog";
            pet = dog;
            cout << pet.breed; //ERROR
        
        Pet *ptrP;
        Dog *ptrD;
        ptrD = new Dog;         
        ptrD->name = "Tommy";
        ptrD->breed = "Kangal Dog";
        ptrP = ptrD;
        cout << ((Dog *)ptrP)->breed; 
        
        class A 
        { 
            int x; 
        };  
        
        class B 
        { 
            B( ) : x(1), c('a') { } 
            int x; 
            char c; 
        };  
        
        int main( ) 
        { 
            A a; 
            B b; 
            a = b;     // b.c == 'a' is "sliced" off
            return 0; 
        }
        
        B b;
        A a = b;
        
        B b1;
        B b2;
        A& a_ref = b2;
        a_ref = b1;
        //b2 now contains a mixture of b1 and b2!
        
        class A {
        public:
          virtual A& operator= (const A& a) {
            assign(a);
            return *this;
          }
        
        protected:
          void assign(const A& a) {
            // copy members of A from a to this
          }
        };
        
        class B : public A {
        public:
          virtual B& operator= (const A& a) {
            if (const B* b = dynamic_cast<const B*>(&a))
              assign(*b);
            else
              throw bad_assignment();
            return *this;
          }
        
        protected:
          void assign(const B& b) {
            A::assign(b); // Let A's assign() copy members of A from b to this
            // copy members of B from b to this
          }
        };
        
                   class baseclass
                  {
                         ...
                         baseclass & operator =(const baseclass&);
                         baseclass(const baseclass&);
                  }
                  void function( )
                  {
                        baseclass obj1=m;
                        obj1=m;
                  }
        
        #include <iostream>
        
        using namespace std;
        
        // Base class
        class A {
        public:
            A() {}
            A(const A& a) {
                cout << "'A' copy constructor" << endl;
            }
            virtual void run() const { cout << "I am an 'A'" << endl; }
        };
        
        // Derived class
        class B: public A {
        public:
            B():A() {}
            B(const B& a):A(a) {
                cout << "'B' copy constructor" << endl;
            }
            virtual void run() const { cout << "I am a 'B'" << endl; }
        };
        
        void g(const A & a) {
            a.run();
        }
        
        void h(const A a) {
            a.run();
        }
        
        int main() {
            cout << "Call by reference" << endl;
            g(B());
            cout << endl << "Call by copy" << endl;
            h(B());
        }
        
        Call by reference
        I am a 'B'
        
        Call by copy
        'A' copy constructor
        I am an 'A'
        
        class Base { 
        int x;
         };
        
        class Derived : public Base { 
         int z; 
         };
        
         int main() 
        {
        Derived d;
        Base b = d; // Object Slicing,  z of d is sliced off
        }
        
        #include<bits/stdc++.h>
        using namespace std;
        class Base
        {
            public:
                int a;
                int b;
                int c;
                Base()
                {
                    a=10;
                    b=20;
                    c=30;
                }
        };
        class Derived : public Base
        {
            public:
                int d;
                int e;
                Derived()
                {
                    d=40;
                    e=50;
                }
        };
        int main()
        {
            Derived d;
            cout<<d.a<<"\n";
            cout<<d.b<<"\n";
            cout<<d.c<<"\n";
            cout<<d.d<<"\n";
            cout<<d.e<<"\n";
        
        
            Base b = d;
            cout<<b.a<<"\n";
            cout<<b.b<<"\n";
            cout<<b.c<<"\n";
            cout<<b.d<<"\n";
            cout<<b.e<<"\n";
            return 0;
        }
        
        [Error] 'class Base' has no member named 'd'
        [Error] 'class Base' has no member named 'e'
        
        void push_back(Action toAdd);
        
        class Base { int x, y; };
        
        class Derived : public Base { int z, w; };
        
        int main() 
        {
            Derived d;
            Base b = d; // Object Slicing,  z and w of d are sliced off
        }