1

我在 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(vector datas) ?

4

4 回答 4

1

类型擦除是(运行时)多态性的一种形式,派生类是从模板生成的。

与所有形式的多态性一样,派生类需要有一些共同点,这些共同点在语法上可以在基类中表达。

编辑:

好的,stijn的回答给了我一个想法。虽然我仍然认为你应该使用现成的东西(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);
  }
}

你说什么?

于 2010-08-27T15:04:30.700 回答
1

你不能定义 SetData(vector),因为 std::vector 需要一个类型,如果你没有 T 的定义,你显然不能在 Base 中定义 SetData(std::vector< T >)。

所以如果你真的需要这个并且认为这是要走的路,你将不得不研究类型调度(或使用 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;
    }
  }
};
于 2010-08-27T14:57:54.177 回答
1

您正在尝试实现类型擦除,然后询问如何让客户端对您正在擦除的类型进行操作。您在此处部署的模式仅适用于派生类Base具有可以在不参考它们存储的具体数据类型的情况下调用的共同操作。如果没有这样的通用操作,并且客户端通过您的Base接口执行的唯一语义上有用的事情涉及使用派生类的具体类型,那么您将无法使用这种设计。

于 2010-08-27T15:12:05.713 回答
0

最终,该类的用户必须知道 T 是什么才能使用派生类型中的数据,即使他们是通过基类型访问它。

您可能可以使用 void* 设置一个通用接口,但您的类的用户仍然必须将每个类实例与特定类型 T 匹配。如果 T 是 int,用户将必须知道集合基于int。

如果你想在向量中存储任何类型,而不用担心哪个实例是哪个类型,你可以让 T void*。如果你这样做,你甚至可以跳过template<typename T>派生的部分。它可以成为一个非模板类。

于 2010-08-27T15:01:10.083 回答