管理生命周期

时间:2012-09-25 10:16:55

标签: c++ templates design-patterns

我创建了一个容器来控制某些类型对象的生命周期(new / delete),以避免任何编程错误。例如,删除对象而不向容器发送任何通知。对象继承自相同的基类(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”派生时报告更好的错误消息的任何“标准方式”
  3. 提前谢谢。

3 个答案:

答案 0 :(得分:2)

  

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

我不知道它是否常见,但看起来很合理。它确保您的容器只能包含指向new分配的对象的指针,这很好。

  

有什么缺点吗?

主要问题是您的容器会破坏Rule of Three。它有一个隐式生成的复制构造函数和复制赋值运算符,它只是复制每个指针;这将为您提供两个容器对象,其析构函数都试图删除相同的对象。

解决这个问题的最简单方法是删除这些成员函数,或者如果您遇到2011版之前的语言,则将其声明为私有(没有实现)。如果您需要能够复制容器,那么您需要实现它们才能安全地进行复制。

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

  

当“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*,应该足够清楚。

答案 1 :(得分:1)

  1. 可以使用template,但使用函数,收到pointer to base-class会更好。
  2. 标准C ++ 11方式 - 将static_assertstd::is_base_of一起使用。没有C ++ 11 - 提升等价物,或你自己的元功能。
  3. 附注:如果您使用smart pointers,则无需编写此类容器

答案 2 :(得分:0)

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

只需将它作为基类指针的普通函数:

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

所以你的来电者

container.addClass( new GreetingDerived() );

而不是

container.addClass<GreetingDerived>();