0

我创建了一个容器来控制某些类型对象的生命周期(新建/删除)以避免任何编程错误。例如,一个对象在没有任何通知容器的情况下被删除。这些对象继承自同一个基类 (GreetingBase)。

对于实现,我使用了“模板技巧”:

class GreetingBase{
public:
  virtual void sayHello(){
    std::cout << "Hello there!" << endl;
  }

  virtual ~GreetingBase(){}
};

class GreetingDerived: public GreetingBase{
public:
  virtual void sayHello(){
    std::cout << "Hola amigos!" << endl;
  }
  virtual ~GreetingDerived(){}
};

class GreetingContainer{
public:
   template <class T>
   void addClass(){
      items.push_back(new T());
   }
~GreetingContainer(){
   for(std::vector<GreetingBase*>::iterator it = items.begin();
   it < items.end(); it++ ){
   delete *it;
 }
}

void talk(){
  for(std::vector<GreetingBase*>::iterator it = items.begin();
    it < items.end(); it++ ){
      (*it)->sayHello();
   }
  }
private:
 std::vector<GreetingBase*> items;
};


int main(){
  GreetingContainer container;
  container.addClass<GreetingDerived>();
  container.talk();
  return 0;
}

问题:

  1. 使用模板来解决这个问题是一种常见的方法吗?有什么缺点吗?
  2. 当“T”不是从“GreetingBase”派生时报告更好的错误消息的任何“标准方式”

先感谢您。

4

3 回答 3

2

使用模板来解决这个问题是一种常见的方法吗?

我不知道这是否普遍,但它看起来足够明智。它确保您的容器只能包含指向用 分配的对象的指针new,这很好。

有什么缺点吗?

主要问题是您的容器违反了三规则。它有一个隐式生成的复制构造函数和复制赋值运算符,它们简单地复制每个指针;这将为您提供两个容器对象,它们的析构函数都试图删除相同的对象。

解决这个问题的最简单方法是删除这些成员函数,或者如果您坚持使用 2011 年之前的语言版本,则将它们声明为私有(没有实现)。如果您需要能够复制容器,那么您需要实现它们以安全地执行此操作。

就个人而言,我会使用智能指针而不是滚动我自己的 RAII 容器;std::unique_ptr如果容器要拥有独占所有权,std::shared_ptr如果您想共享所有权,或者可能std::weak_ptr持有对由共享指针在其他地方管理的对象的非拥有引用。如果您被困在过去,那么unique_ptr将无法使用,但 Boost 提供了shared_ptrweak_ptr以及与您类似的指针容器。

当“T”不是从“GreetingBase”派生时报告更好的错误消息的任何“标准方式”

在 C++11 中,您也许可以使用静态断言,例如:

static_assert(std::is_base_of<GreetingBase, T>::value, 
              "Wrong type for GreetingContainer");

或者,您可能会通过创建本地指针获得更易读的错误消息;那么错误消息至少不会包含的全名push_back

GreetingBase * p = new T();
items.push_back(p);

错误消息将类似于can't convert Whatever* to GreetingBase*,应该足够清楚。

于 2012-09-25T10:28:10.807 回答
1
  1. 可以template用于此,但使用接收pointer to base-class的功能更好。
  2. 标准 C++11 方式 -static_assertstd::is_base_of 一起使用。没有 C++11 - boost 等价物或您自己的元函数。

旁注:如果您使用smart pointers,则无需编写此类容器

于 2012-09-25T10:20:37.577 回答
0

我认为制作addClass模板没有优势;它只是意味着您获得了为每种不同类型实例化的代码的新副本,并且您正在执行特定的构造函数调用。

只需使其成为一个采用基类指针的普通函数:

void addClass(GreetingBase *o){
    items.push_back(o);
}

所以你的来电者做

container.addClass( new GreetingDerived() );

代替

container.addClass<GreetingDerived>();
于 2012-09-25T10:24:45.250 回答