什么是对象切片(Object Slicing)?

关于对象切片Thinking in C++中有这么一段话

英文原版:

If you upcast to an object instead of a pointer or reference,
something will happen that may surprise you: the object is “sliced”
until all that remains is the subobject that corresponds to the
destination type of your cast.

对于绿色这一段话,有些书译为:这个对象被”切片”,直到剩下的是适合于目的的子对象。//有点晦涩。。。

我的理解是:这个对象将被”分割”成只剩下与目的类型(即:转换以后的类型)相匹配的子对象(即:成员变量和函数)

这句话的意思也就是说:在函数传参处理多态性时,如果一个派生类对象在UpCasting时,用的是传值的方式,而不是指针和引用,那么,这个派生类对象在UpCasting以后,将会被slice(切分)成基类对象,也就是说,派生类中独有的成员变量和方法都被slice掉了,值剩下和基类相同的成员变量和属性。这个派生类对象被切成了一个基类对象。

那么我们来看个实例,真正理解什么是对象切片,代码如下:摘自Thiking In C++

view plaincopy to clipboardprint?
//: C15:ObjectSlicing.cpp  
#include <iostream>  
#include <string>  
using namespace std;  
class Pet {  
    string pname;  
public:  
    Pet(const string& name) : pname(name) {}  
    virtual string name() const { return pname; }  
    virtual string description() const {  
        return "This is " + pname;  
    }  
};  
class Dog : public Pet {  
    string favoriteActivity;  
public:  
    Dog(const string& name, const string& activity)  
        : Pet(name), favoriteActivity(activity) {}  
    string description() const {  
        return Pet::name() + " likes to " +  
            favoriteActivity;  
    }  
};  
void describe(Pet p) { // Slices the object  
    cout << p.description() << endl;  
}  
int main() {  
    Pet p("Alfred");  
    Dog d("Fluffy", "sleep");  
    describe(p);    //正常调用基类函数  
    describe(d);    //对象切片  
}  
//: C15:ObjectSlicing.cpp
#include <iostream>
#include <string>
using namespace std;
class Pet {
 string pname;
public:
 Pet(const string& name) : pname(name) {}
 virtual string name() const { return pname; }
 virtual string description() const {
  return "This is " + pname;
 }
};
class Dog : public Pet {
 string favoriteActivity;
public:
 Dog(const string& name, const string& activity)
  : Pet(name), favoriteActivity(activity) {}
 string description() const {
  return Pet::name() + " likes to " +
   favoriteActivity;
 }
};
void describe(Pet p) { // Slices the object
 cout << p.description() << endl;
}
int main() {
 Pet p("Alfred");
 Dog d("Fluffy", "sleep");
 describe(p); //正常调用基类函数
 describe(d); //对象切片

在Slice前后,Dog类对象d的变化如下图:

 

注意到after Slice以后,虚函数表指针时Pet vptr,Dog类的favoriteActivity成员变量也不存在了。

其实这时候执行了一些步骤:

在正常情况下,main函数中执行describe函数,在传递Dog对象d的时候,调用了Pet类的拷贝构造函数,相当于此时,在describe函数中的那个Pet类对象p是拷贝构造的结果,所以,跟Dog对象d已经没有了关系。所以,此时,p.description()执行的会是调用的基类也就是Pet类的description函数。而也许,这并不是你所想要看见的结果(因为,你或许是想实现多态调用)

但是,此例中,恰恰因为Pet类中的description函数被声明为纯虚函数,它是不能被创建实例的,所以,在全局的describe函数中不可能有者拷贝构造函数来实例化一个Pet类对象。所以,此时,编译器就会报错。

而这种情况,也是纯虚函数存在的一个重要意义:避免对象切片。

纯虚函数的一个重要意义,就在于可以在编译期间避免对象切片,从而避免很多可能会出现的问题。


From:http://blog.csdn.net/beckle_ye/archive/2009/10/19/4700612.aspx

你可能感兴趣的:(object,String,Class,iostream,reference,编译器)