关于智能指针,一般有两种常用的策略,参考:智能指针;有一篇原创的智能指针写得不错:一个智能实现的改进。
关于对象工厂,看到一篇写的不错的博文:泛化对象工厂简述。
在 ITK 中将智能指针与对象工厂结合。除少部分非常小的类之外,其它类都继承自 LightObject 或者 Object(其中,Object 也继承自 LightObject)。LightObjec 是 ITK 中最基层的轻量极类,它提供了增加/减少引用计数(Register()/UnRegister())以及打印对象信息(Print())的方法。ITK 中每个类都提供如下标准定义与函数:
typedef classname Self; //表示自身
typedef parentClass Superclass; //表示父类;LightObject 是最顶基类,无此项
typedef SmartPointer<classname> Pointer; //将该对象的指针包装成智能指针,供用户使用。
typedef SmartPointer<const classname> ConstPointer; //const SmartPointer
static Pointer New(); //除了 LightObject 以外,其余类都是以宏定义 itkNewMacro(Self); 自动生成。
ITK中,用户创建对象实例还是通过 new 操作符,而是以统一的格式:ClassName::Pointer pclass = ClassName::New(); 即可得到指向该类实例对象的智能指针 pclass。其中,New() 通过使用对象工厂创建对象实例。因为智能指针的构造函数是 public 的,所以如上语句得到的智能指针是在“栈”上创建的,而其指向的对象实例则是在“堆”上创建的,所以当智能指针离开作用域时,就会自动析构。此时并不直接删除其所指向的对象,而是减少其引用计数,并且判断引用计数是否为 0,如果为 0 才删除。用户操作得到的智能指针,无论是进行拷贝、还是赋值都不会创建该对象的副本,而是增加该对象的引用计数,这样便实现了指针指向的对象的共享。
对象工厂使得用户能够在运行时进行版本替换,程序中我们进行了演示。
下面给出 ITK 中智能指针(SmartPointer)、以及对象工厂(ObjectFactory)的代码,这里的代码基本都是直接来自 ITK 源码,作了适当的裁减,里面做了比较详细的注释,其实现非常简洁漂亮,很值得借鉴。对象工厂部分比较麻烦,需要仔细研究。
1.几个宏,用于在类中生成统一的 New()、CreateAnother()、GetNameOfClass() 方法。
//MyMacro.h #pragma once //该宏会在类中生成两个用于创建对象的成员函数: //1.static Pointer New(); 客户调用, 用于创建对象的接口, 使用对象工厂创建 //如果使用对象工厂创建失败, 则使用 new 操作符创建对象. //New() 将一个原始指针赋值给 SmartPointer, 然后再调用原始指针的 UnRegister() //减少引用计数, 因为 MyObject 的构造函数会将引用计数初始化为 1. //2.virtual MyObject::Pointer CreateAnother(void) const; 利用现有对象创建一个完全相同的对象 //这在需要向上类型转换时非常有用. #define MyNewMacro(x) / static Pointer New(void)/ {/ Pointer smartPtr = MyObjectFactory<x>::Create(); / if(smartPtr.GetPointer() == NULL) / { / smartPtr = new x; / } / smartPtr->UnRegister(); / return smartPtr; / } / virtual MyObject::Pointer CreateAnother(void) const / { / MyObject::Pointer smartPtr; / smartPtr = x::New().GetPointer(); / return smartPtr; / } //该宏会在类中生成两个用于创建对象的成员函数,但不使用对象工厂: //1.static Pointer New(); 客户调用, 用于创建对象的接口, 注: 不使用对象工厂. //New() 将一个原始指针赋值给 SmartPointer, 然后再调用原始指针的 UnRegister() //减少引用计数, 因为 MyObject 的构造函数会将引用计数初始化为 1. //2.virtual MyObject::Pointer CreateAnother(void) const; 利用现有对象创建一个完全相同的对象 #define MyFactorylessNewMacro(x) / static Pointer New(void) / { / Pointer smartPtr; / x *rawPtr = new x; / smartPtr = rawPtr; / rawPtr->UnRegister(); / return smartPtr; / } / virtual MyObject::Pointer CreateAnother(void) const / { / MyObject::Pointer smartPtr; / smartPtr = x::New().GetPointer(); / return smartPtr; / } //运行时类型识别. //virtual const char* GetNameOfClass() const; #define MyTypeMacro(thisClass,superclass) / virtual const char *GetNameOfClass() const / {/ return #thisClass;/ }
2.临界区类,用于控制引用计数的增加与减少,保证其操作的原子性。
//MyFastMutexLock.h #pragma once //不同的平台需要定义不同的类型,这里只给出 windows32 例子 //不同平台在处理时有相应的优化策略, 以保证操作的原子性. #if defined(_WIN32) #include "Windows.h" #include <winbase.h> typedef CRITICAL_SECTION FastMutexType; #endif //为了演示, 完全可以只使用 int 即可, 但无法提供真正的加锁功能 #ifndef _WIN32 typedef int FastMutexType; #endif //临界区锁类: 在修改临界值时, 对其加锁, 以保证修改的唯一原子性. class MyFastMutexLock { public: typedef MyFastMutexLock Self; //可以在栈上创建临界区对象, 所以将构造函数与析构函数设为 pubilc. MyFastMutexLock(); ~MyFastMutexLock(); //Lock, 加锁, 使其它任何对象都不能修改被加锁的对象 void Lock() const; //Unlock, 解锁 void Unlock() const; protected: mutable FastMutexType m_FastMutexLock; };
3.回调函数,与供对象工厂使用
//MyCreateObjectFunction.h #pragma once #include "MyObject.h" //MyCreateObjectFunctionBase, 抽象基类. //定义创建对象的回调函数(callback) class MyCreateObjectFunctionBase: public MyObject { public: typedef MyCreateObjectFunctionBase Self; typedef MyObject Superclass; typedef MySmartPointer<Self> Pointer; typedef MySmartPointer<const Self> ConstPointer; //创建一个对象,并返回一个指向该对象的指针. virtual MySmartPointer<MyObject> CreateObject() = 0; protected: MyCreateObjectFunctionBase() { } ~MyCreateObjectFunctionBase(){ } private: MyCreateObjectFunctionBase(const Self&); void operator=(const Self&); }; //MyCreateObjectFunction: //具体的用于创建对象的回调函数, 与 "对象工厂" 一起使用. template <class T> class MyCreateObjectFunction : public MyCreateObjectFunctionBase { public: typedef MyCreateObjectFunction Self; typedef MyCreateObjectFunctionBase Superclass; typedef MySmartPointer<Self> Pointer; typedef MySmartPointer<const Self> ConstPointer; //用于创建 MyObject 对象的方法. MyFactorylessNewMacro(Self); MyObject::Pointer CreateObject() { //std::cout << "MyCreateObjectFunction::CreateObject() ..." << std::endl; return T::New().GetPointer(); } protected: MyCreateObjectFunction() { } ~MyCreateObjectFunction() { } private: MyCreateObjectFunction(const Self&); void operator=(const Self&); };
4.MySmartPointer:智能指针
//MySmartPointer.h #pragma once #include <iostream> // //在使用智能指针的类中,使用 typedef MySmartPointer<MyClass> Pointer; 将其包装成智能指针 //声明并定义方法静态方法New()它使用对象工厂创建对象实例(堆上),并返回一个指向该实例的智能指针. //而且将该类的构造函数声明成保护类型,客户只能通过 New() 创建对象,且不需要手动释放。 //通过使用智能指针, 拷贝函数以及赋值操作符并不真正生成新的对象实例,从而实现指针指向对象的共享 //智能指针的构造函数是 public,所以能够在栈上分配,当离开作用域时智能指针会被自动删除. //当该智能指针指向对象的引用计数减为 0 时, 才会真正删除其指向的对象. // //智能指针是模拟指针的行为,只要提供如下几个相应的实现即可: //构造函数、析构函数、operator=、operator->、operator* //智能指针自动调用对象的 Register()/UnRegister() 增加或减少引用计数 //关键的地方有几点: //1.New() 通过对象工厂创建实例, 对象工厂的实现比较麻烦. //2.Register()/UnRegister() 增加与减少引用计数, 它调用了被包装对象的同名函数. // 增加或减少引用计数需要考虑"临界值"的竞争问题, 不同平台有不同的实现. //3.封装的指针应该隐式转换至原始的指针类型吗? 例如通常函数的参数接收的是原始 // 指针类型的参数. 应该允许这种隐式转换吗?因为隐式转换有潜在的危险(如被人不小心 // delete 等). 所以实现中使用一个函数来取得原始指针. 而不是用转换函数来隐式转换. //4.拷贝构造函数: 增加右操作数的引用计数. //5.赋值操作符(=): 增加右操作数的引用计数, 并减少左操作数的引用计数. //----------------------------------------------------------------------------------- //智能指针的关键技术在于: 构造栈上对象的生命期控制堆上构造的对象的生命期. //因为在智能指针的内部,存储着堆对象的指针,而且在构析函数中调用delete行为 //即,智能指针在栈上构建, 而其存储的指针指向的对象则在堆上构建. //----------------------------------------------------------------------------------- //MySmartPointer 智能指针,通过重载 operator-> 、* 、= 实现智能指针. //MySmartPointer 将某个"对象的指针"进行包装,使其成为一个智能指针 template <class TObjectType> class MySmartPointer { public: typedef TObjectType ObjectType; //默认构造函数, 将构造函数设为 public, 智能指针可能在栈上分配. //ITK 中大部分类的构造函数都为 protected, 所以不能在栈上创建对象 //只能通过调用调用方法 New(), 而 New() 通过对象工厂创建对象 //并返回一个智能指针控制其所指向对象的生存周期. MySmartPointer () { //std::cout << "Smart 默认构造函数 /n"; m_Pointer = 0; } //有参构造函数, 增加其指向对象的引用计数, 共享指向的对象 MySmartPointer (ObjectType *p): m_Pointer(p) { //std::cout << "Smart 有参构造函数: "; this->Register(); } //拷贝构造函数, 增加引用计数. MySmartPointer (const MySmartPointer<ObjectType> &p) : m_Pointer(p.m_Pointer) { //std::cout << "Smart 拷贝构造函数: "; this->Register(); //并不真正创建对象,只是增加引用计数. } //Destructor: //智能指针的关键技术:在于构造栈上对象的生命期控制堆上构造的对象的生命期. //因为在智能指针的内部,存储着堆对象的指针,而且在构析函数中调用delete行为 //即,智能指针在栈上构建, 而其存储的指针指向的对象则在堆上构建. ~MySmartPointer () { //std::cout << "~Smart 析构函数: /n"; this->UnRegister(); m_Pointer = 0; } //重载操作符 operator -> 以及 *, 这两个操作符以及 = 是实现智能指针的关键. ObjectType *operator -> () const { return m_Pointer; } //返回指向对象的指针 operator ObjectType * () const //注意其形式 { return m_Pointer; } //测试指针是否已经实例化 bool IsNull() const{ return m_Pointer == 0; } //操作会重载,使用模板; 注意, 在模板类的内部再使用模板时,其名称应该不同 template <typename TR> bool operator == ( TR r ) const { return (m_Pointer == static_cast<const ObjectType*>(r) ); } template <typename TR> bool operator != ( TR r ) const { return (m_Pointer != static_cast<const ObjectType*>(r) ); } //*/ //取得原始指针. ObjectType *GetPointer () const { return m_Pointer; } //重载符值操作符 =, 参数为另一个智能指针. MySmartPointer &operator = (const MySmartPointer &r) { return this->operator = (r.GetPointer()); //调用了下面的 = 重载形式 } //重载符值操作符, 参数为原始对象指针 MySmartPointer &operator = (ObjectType *r) { //std::cout << "Smart 赋值 =, 左减, 右增 " << std::endl; if (m_Pointer != r) { //如下操作是在原始指针上 ObjectType* tmp = m_Pointer; //避免循环地解引用(UnRegister) m_Pointer = r; this->Register(); //1.增加右操作数的引用计数,因为 m_Pointer 指向了 r if ( tmp ) { tmp->UnRegister(); //2.减少左操作数的引用计数 } } return *this; } private: //要进行包装的对象 ObjectType* m_Pointer; //Register() 增加引用计数; UnRegister() 减少引用计数. //引用计数在 ObjectType 中. void Register() { if(m_Pointer) { m_Pointer->Register(); } } void UnRegister() { if(m_Pointer) { m_Pointer->UnRegister(); } } };
5.MyObject 所有对象的基类,实现引用计数功能。
//MyObject.h
#pragma once
#include "MyMacro.h"
#include "MyFastMutexLock.h"
#include "MySmartPointer.h"
#if defined(_WIN32)
//To get LONG defined
#include "Windows.h"
#endif
//MyObject 几乎是所有对象的基类, 实现引用计数功能,
//代码基本来 ITK 中的 LightObject 类, LightObject 在 ITK 中是最顶层的类.
class MyObject
{
public:
typedef MyObject Self;
typedef MySmartPointer<Self> Pointer; //包装为智能指针
typedef MySmartPointer<const Self> ConstPointer;
//通过对象工厂创建对象的实例
static Pointer New();
//根据已存在的对象创建一新的对象实例,允许用户创建一个完全相同的对象
//这在需要向上类型转换时非常有用***
virtual Pointer CreateAnother() const;
//使用 New() 的对象, 应该使用 Delete()
//Delete() 会调用 UnRegister 减少引用计数
//只有当该对象的引用计数减为 0 时才会真正删除对象
virtual void Delete();
//运行时类型识别, 返回某对象的具体类名.
virtual const char *GetNameOfClass() const
{return "MyObject";}
//Used to avoid dll boundary problems.
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*);
void operator delete[](void*, size_t);
//增加与减少引用计数
virtual void Register() const;
virtual void UnRegister() const;
//返回该类对象实例的引用计数
virtual int GetReferenceCount() const
{ return static_cast<int>(m_ReferenceCount); }
//设置该类对象的引用计数值.
virtual void SetReferenceCount(int);
protected:
MyObject() : m_ReferenceCount(1) { }
virtual ~MyObject();
//不同的平台, 需要定义不同的类型, 这里只简单使用 int 型. 引用计数.
#if (defined(WIN32) || defined(_WIN32))
typedef LONG InternalReferenceCountType;
#else
typedef int InternalReferenceCountType;
#endif
//引用计数
mutable InternalReferenceCountType m_ReferenceCount;
//mutable int m_ReferenceCount;
//临界区锁, 用于对临界区变量加锁
mutable MyFastMutexLock m_ReferenceCountLock;
private:
MyObject(const Self&);
void operator=(const Self&);
};