转载地址:http://blog.sina.com.cn/s/blog_3c6889fe0100tzq8.html

 

当把一个派生类对象赋给一个基类对象时,会发生对象切割(另外用基类对象强制转换派生类对象也会)

 

 

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

#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);    //正常调用基类函数   This is Alfred
    describe(d);    //对象切片           This is Fluffy
 
 return 0;
}

 

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

对象切片

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

 

按照正常的想法,将子类对象d赋给父类p时,应该将d中所有p中存在的元素赋值给p,d中存在p中不存在的元素则被切割掉。由于d和p中都有虚函数,故都存在一个指向各自对象的虚函数表的指针vptr。

那此时是否将子类d的vptr赋值给p的vptr呢:p.vptr = d.vptr,这是错误的。因为子类d的vptr指向的虚函数表可能存在父类p中不存在的虚函数,故这样的转换是不对的。

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

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

 

一个可以避免发生对象切片的方法就是将父类的虚函数定义成纯虚函数。

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