boost之any解析
我们描述一种事物,就必须要有这种事物特定的属性和方法,不同的事物,它们的属性和方法都是不一样的,这个对于单个事物来说还比较好描述,一个事物一个实例,但是但出现批量的时候,事物过多并且还有很多种未知的事物可能出现,这个时候考虑到框架的可适应性,很多人都会很纠结。
假设类型A,class A。我们用一个数组vector去存放这样的一组数据类型,然后我们知道这个vector中的每一个数据都是类型A的,这样我们就可以去方便的操作它,添加、删除、获得其中一项调用其接口、为其分配内存等等,这些操作的前提都是我们确定的知道我们的vector中存放的就是A,这里万一有一天需求变更,除了A之外还要再次存放数据B,这个时候就不是很好办了,有人就会说我把两个对象抽象一下,并且我们存放的是对象的指针,这里我们就不能是Vector<A>,而要变成vector<AB *>,其中AB是A、B公用的父类,貌似能够解决这个问题了,但是这种结局方式有几个很大的问题,首先可扩展性不是很好,万一需求又来了,出现了一个新的类C也要存放到这个列表中,然后你又会花时间想办法在抽象一个更大的抽象类ABC,整个代码都要调整,这样工作量也很大。另外一个问题就是,A、B、C本身就不是具有共性的一类东西,抽象的太厉害,父类就没有什么东西了,那么我们A、B、C三个子类各自特定的属性和方法就很难去调用了。这样就出现了这样的一个问题。
我们知道不管是什么类,放在内存里面我们用指针就可以去访问,不管你的类的大小或者派生层次等,所有类的指针都是一样的,32位平台就是一个32的地址数据,64位的平台就是一个64位的数据,这样来说最抽象的东西就是Void *指针,这样就抽想到极致了,我们就不会因为又来了一个D类而想办法去抽象D的一些属性,直接一个vector<void *>来表述我们存放的数据,这样无论数据类型有多少,我以不变应万变。
但是上面的做法会抹杀所有具体类型的特性,就好像男人和女人都是人,那还区分男人和女人干什么。所以这里通过void *来抽象做的太绝对了,又没有一种方式既可以做到包容,又可以做到不抹杀特性。这里我们的Any就出现了。
我们构建一个类叫做Any,可以封装所有的类,现有的或者将来会有的都可以封装在里面。我们知道类有一个最大的公用特点就是都要放在内存,凡在内存就会有指针,所以Any的一个重要的内容就是封装指针,其次虽然都是人,但也会有男人和女人、中国人和美国人之分,这里我们要封装的第二种东西就是特性,中国人是黄皮肤,美国人是白皮肤,这样我们要封装一个表示特性的参数,这个就是一个Type。这样我们在调用者和实际被调用者之间加入了一层过渡层,可以理解为门面模式。
Any
{
Void*;
TypeId
}
上面的就是我们Any做的事情。在boost中,这个Any充分的利用了模板的一些特性来实现。下面简单的分析源码。
首先我们要存放这个最关键的两项数据:
class placeholder
{
public: // structors
virtual ~placeholder()
{
}
public: // queries
virtual const std::type_info& getType() const = 0; //得到类型
virtual placeholder * clone() const = 0; //可以复制
virtual void writeToStream(std::ostream& o) = 0; //输出流 这个方法其实可以不要
};
然后按照这个接口我们来实现我们的模板:
template<typename ValueType>
class holder : public placeholder
{
public: // structors
holder(const ValueType & value) //构造函数里面要有类型输入
: held(value)
{
}
public: // queries
virtual const std::type_info & getType() const 返回数据类型
{
return typeid(ValueType);
}
virtual placeholder * clone() const //更具类型自我复制
{
return OGRE_NEW_T(holder, MEMCATEGORY_GENERAL)(held);
}
virtual void writeToStream(std::ostream& o) //输出
{
o << held;
}
public: // representation
ValueType held;
};
然后我们看看Any的实现:
我们的Any类中只有一个成员就是
placeholder * mContent; 这里是以指针的方式来存放,通过它首先可以得到我们的数据类型,然后也可以得到指针地址,其实相当于在原始把void *变成了placeholder *两个都是最抽象的指针,不过后者还可以得到指针指向内容的类型。
这样我们在给这个Any类一些其他的方法,让她可以方便的使用。
class Any
{
public: // constructors
Any()
: mContent(0) //指向一片虚无
{
}
template<typename ValueType> //构造函数一定要显示的调用 调用的时候还有有具体类型。然后我们就会new出这样的一个对象
explicit Any(const ValueType & value)
: mContent(OGRE_NEW_T(holder<ValueType>, MEMCATEGORY_GENERAL)(value))
{
}
Any(const Any & other) //可以从其他地方克隆过来
: mContent(other.mContent ? other.mContent->clone() : 0)
{
}
virtual ~Any()
{
destroy();
}
public: // modifiers
Any& swap(Any & rhs) //交换一下,你只想A我来指向B,交换包括指针和类型的同时交换。
{
std::swap(mContent, rhs.mContent);
return *this;
}
template<typename ValueType>
Any& operator=(const ValueType & rhs)
{
Any(rhs).swap(*this);
return *this;
}
Any & operator=(const Any & rhs)
{
Any(rhs).swap(*this);
return *this;
}
后面略。