我在C ++中使用了类型擦除模式,即我隐藏了一个带有抽象类的模板类
class Base{
virtual ~Base(){}
//pure virtual methods...
};
template<typename T>
class Derived : Base{
Derived<T>(){}
~Derived(){}
//public methods...
private :
vector<T> datas;
};
问题:如果我想检索或修改数据,我必须使用Base类
如何设置访问器getDatas()和SetDatas(矢量数据)?
答案 0 :(得分:1)
你不能定义一个SetData(向量),因为std :: vector需要一个类型,如果你没有T的定义,你很难在Base中定义SetData(std :: vector&lt; T&gt;)。
因此,如果你真的需要这个,并认为这是要走的路,你将不得不考虑类型调度(或使用void *进行黑客攻击)。 Boost在某些地方使用类型调度,否则谷歌提供示例。
编辑它的外观简单示例;不是真正的类型调度,而是更直接的
class Base
{
public:
template< class T >
bool SetData( const std::vector< T >& t )
{
return SetData( static_cast< const void* >( &t ), typeid( t ) );
}
protected:
virtual bool SetData( const void*, const std::type_info& ) = 0;
};
template< class T >
class Derived : public Base
{
protected:
bool SetData( const void* p, const std::type_info& info )
{
if( info == typeid( std::vector< T > ) )
{
const std::vector< T >& v = *static_cast< const std::vector< T >* >( p );
//ok same type, this should work
//do something with data here
return true;
}
else
{
//not good, different types
return false;
}
}
};
答案 1 :(得分:1)
类型擦除是(运行时) 多态 的一种形式,其中派生类是从模板生成的。
与所有形式的多态性一样, 派生类需要具有共同的东西 ,这可以在语法上在基类中表达。
<强> 编辑: 强>
好的,stijn's answer给了我一个主意。虽然我仍然认为你应该使用现成的东西(boost::any
),但只是为了踢,这是一个草图:
class Base
{
public:
template< class T >
bool SetData(const std::vector<T>& data)
{
return SetData(&data,typeid(T));
}
private:
virtual bool SetData(const void* data, const std::type_info& tid) = 0;
}
template< class T >
class Derived : public Base
{
public:
bool DoSetData(const std::vector<T>& data)
{
// tbd
}
private:
virtual bool SetData(const void* data, const std::typeinfo& tid)
{
if( tid != typeid(T) )
return false;
const std::vector<T>* pdata = reinterpret_cast<const std::vector<T>*>(data);
return DoSetData(*pdata);
}
}
你说什么?
答案 2 :(得分:1)
您正在尝试实现类型擦除,然后询问如何让客户端对您正在擦除的类型进行操作。如果Base
的派生类具有可以在不参考它们存储的具体数据类型的情况下调用的共同操作,那么您在此部署的模式是合适的。如果没有这样的常见操作,并且客户端通过Base
接口执行的唯一语义上有用的事情涉及使用派生类的具体类型,那么您将无法使用此设计。
答案 3 :(得分:0)
最终,类的用户必须知道T是什么才能使用派生类型中的数据,即使他们通过基类型访问它。
您可以使用void *设置一个有点通用的接口,但是您的类的用户仍然必须将每个类实例与特定类型T匹配。如果T是int,则用户将拥有要知道该集合是基于int。
如果要在向量中存储任何类型,而不必担心哪个类型是哪种类型,则可以使T void *。如果你这样做,你甚至可以跳过派生的template<typename T>
部分。它可能会成为非模板类。