一、容器与继承
在容器中保存有继承关系的对象时,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始化)。
唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。C++中一个通用的技术是包装类(cover)或句柄类(handle)。用句柄类存储和管理类指针。
句柄类大体上完成两方面的工作:
-
管理指针,这与智能指针的功能类似。
-
实现多态,利用动态绑定,是得指针既可以指向基类,也可以指向派生类。
包装了继承层次的句柄有两个重要的设计考虑因素:
-
像对任何保存指针的类一样,必须确定对复制控件做些什么。包装了继承层次的句柄通常表现得像一个智能指针或者像一个值。
-
名柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用户必须了解和使用基本层次中的对象(objects in theunderlying hierarchy)。
下面通过一个我自己写的一个简单的例子来说明这个问题:
这个例子程序包括一个基类,一个派生类,还有一个句柄类。
其中,基类有2个私有成员,数值m_base和程序名字name。派生类有一个新的私有成员,m_der。
派生类和基类有虚函数compute。基类的compute它计算基类成员m_base平方。派生类的compute计算m_base平方和m_der之和。
句柄类有两个数据成员,分别是指向引用计数的指针( 这里必须是指针,复制时引用计数复制指针的值,保证一个实例化对象只有一个引用计数)和指向基类或者是其派生类的指针。
#include<iostream> #include<string> #include<exception> using namespace std; // base class class Base { public: //basic constructor Base(int m_base = 1, string name = "Base") : m_base(m_base), name(name) { cout << "Base constructor called!" << endl; } //copy constructor Base(const Base &base) : Base(base.m_base, base.name) { cout << "Base copy called" << endl; } virtual Base *clone() const { return new Base(*this); } const string getName() { return name; } virtual int compute() const { return m_base * m_base; } virtual ~Base(){ cout<<"Base deleted"<<endl; } protected: int m_base; string name; }; class Derived : public Base { public: //basic constructor Derived(int m_base, string name, int m_der) : Base(m_base, name), m_der(m_der) { cout << "Derived constructor called" << endl; } //copy constructor Derived(const Derived &derived) : Derived(derived.m_base, derived.name, derived.m_der) { cout << "Derived copy called" << endl; } virtual Derived *clone() const { return new Derived(*this); } virtual int compute() const { //调用父类中定义的方法 return Base::compute() + m_der; } virtual ~Derived(){ cout<<"Derived deleted"<<endl; } private: int m_der; }; class Handler { public: //默认构造函数 Handler() : pBase(NULL), use(new int(1)) { } //一般构造函数 Handler(const Base &item) : pBase(item.clone()), use(new int(1)) { } //复制构造函数 //每复制一次,引用计数就加1 Handler(const Handler &ref) : pBase(ref.pBase), use(ref.use) { ++*use; } //重载赋值操作符 Handler &operator=(const Handler &right) { ++*(right.use); decrese_use(); pBase = right.pBase; use = right.use; return *this; } //重载箭头操作符 const Base *operator->() const { if (pBase) return pBase; else throw logic_error("unbound Handler!"); } //重载解引用操作符 const Base &operator* () const{ if(pBase) return *pBase; else throw logic_error("unbound Handler"); } void print_use() { cout << pBase->getName() << " use: " << *use << endl; } //析构函数 ~Handler() { decrese_use(); } private: //此处必须使用指针,保证一个Base实例只对应一个引用计数 int *use; Base *pBase; void decrese_use() { if (--*use == 0) { cout << pBase->getName() << " is going to be deleted!" << endl; delete pBase; } } }; int main() { Handler h1(Base(2,"Base")); h1.print_use(); cout<<"Base compute:"<<(*h1).compute()<<endl; Handler h2(h1); h2.print_use(); cout<<"Base compute:"<<(*h2).compute()<<endl; cout<<"-------------------------------------"<<endl; Handler h3(Derived(3,"derived",3)); h1=h3; h1.print_use(); cout<<"Derived compute:"<<(*h1).compute()<<endl; cout<<"system automatic delete begin"<<endl; return 0; }
二、句柄类
句柄类Handle 有3个构造函数:默认构造函数,复制构造函数,和接收基类Base对象的构造函数。为了保证 在接收基类Base对象的构造函数中 复制具体对象的时候实现动态调用,得到正确类别的实例,我们在类中定义了虚函数clone。
Base
virtual Base *clone() const { return new Base(*this); }
Derived
virtual Derived *clone() const { return new Derived(*this); }
三、运行结果
主函数调用:
int main() { Handler h1(Base(2,"Base")); h1.print_use(); cout<<"Base compute:"<<(*h1).compute()<<endl; Handler h2(h1); h2.print_use(); cout<<"Base compute:"<<(*h2).compute()<<endl; cout<<"-------------------------------------"<<endl; Handler h3(Derived(3,"derived",3)); h1=h3; h1.print_use(); cout<<"Derived compute:"<<(*h1).compute()<<endl; cout<<"system automatic delete begin"<<endl; return 0; }
输出:
Base constructor called! Base constructor called! Base copy called Base deleted Base use: 1 Base compute:4 Base use: 2 Base use: 2 Base compute:4 ------------------------------------- Base constructor called! Derived constructor called Base constructor called! Derived constructor called Derived copy called Derived deleted Base deleted derived use: 2 derived use: 2 Derived compute:12 system automatic delete begin Base is going to be deleted! Base deleted derived is going to be deleted! Derived deleted Base deleted
主函数中使用Base对象创建了Handler对象h1,并由h1构造Handler对象h2,通过输出可以发现Handler对象的引用计数由1变为2。然后使用Derived对象创建Handler对象h3,并将其赋值给h1,对h1,h3 输出其引用计数,可知引用计数均为2.