由vector造成的对象切片
在编写一个小实例的时候,发现用vector存放基类,而试图调用子类的虚函数是行不通的!代码如下:
#include <iostream> #include <vector> using namespace std; class base { public: base(void){}; virtual ~base(void){} base(const base& _rhs){}; base& operator=(const base& _rhs) { if(this != &_rhs) { } return *this; }; virtual void show(void) { std::cout << "I'm Base"<< std::endl; }; private: }; class child : public base { public: child(void){}; virtual ~child(void){} child(const child& _rhs){}; child& operator=(const child& _rhs) { if(this != &_rhs) { } return *this; }; virtual void show(void) { std::cout << "I'm Child"<< std::endl; }; }; typedef vector<base> VecType; //声明的是存放基类的vector int main(void) { VecType v1; //分别存放一个基类和子类 v1.push_back(base()); v1.push_back(child()); VecType::iterator it = v1.begin(); for(;it!=v1.end();++it) { //调用虚函数show() //输出的是: //I'm Base //I'm Base it->show(); } return 0; }
这样的结果是我当初的构想完全不一样的,那么为什么会造成这种现象呢?
先从对象大小的角度来看:
由于声明的是vector<base>,那么每个元素的大小就是sizeof(base),如果转入的是子类,那实际上进入vector的“合法”大小也仅为sizeof(base),超过这部分的大小,将被忽略,这就是对象切片(Object Slicing)。
再从虚函数表的角度来看:
我们知道,C++实现多态的原理就是利用了虚函数表,每个对象在定义后的头4个字节就是一个指向虚函数表的指针(而虚函数表则是每个类共享一份),这是在编译器就确定的,在运行时再通过虚函数表的指针找到真正的实现函数的首地址。那么心细的朋友可能发现了,代码:
v1.push_back( child() );
明明定义了一个child对象,也就是在临时对象中的虚函数表指针是指向child的show函数的,那么放入v1的对象也应该是拥有指向child的虚函数表指针的吧?
答案是否定的,我们来看push_back的代码(GCC):
void push_back(const value_type& __x) { if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) { this->_M_impl.construct(this->_M_impl._M_finish, __x);//这里重新调用的是base的复制构造函数 ++this->_M_impl._M_finish; } else _M_insert_aux(end(), __x); }
就是说,其实在vector中存放的已经是彻底的base了。。。
因此通过声明vector<base>来实现多态是不行的。
那么如果才能通过vector来实现多态呢?
可以用存放指针的方法:vector<base*> 示例代码如下:
#include <iostream> #include <vector> using namespace std; class base { public: base(void){}; virtual ~base(void){} base(const base& _rhs){}; base& operator=(const base& _rhs) { if(this != &_rhs) { } return *this; }; virtual void show(void) { std::cout << "I'm Base"<< std::endl; }; private: }; class child : public base { public: child(void){}; virtual ~child(void){} child(const child& _rhs){}; child& operator=(const child& _rhs) { if(this != &_rhs) { } return *this; }; virtual void show(void) { std::cout << "I'm Child"<< std::endl; }; private: char m_c; }; typedef vector<base*> VecType; int main(void) { VecType v1; base* p1 = new base(); base* p2 = new child(); v1.push_back(p1); v1.push_back(p2); VecType::iterator it = v1.begin(); for(;it!=v1.end();++it) { (*(it))->show(); } return 0; }
当然在实际项目中用智能指针会是更好的选择!