管理生命周期

Managing life cycle

本文关键字:周期 生命 管理      更新时间:2023-10-16

我创建了一个容器,用于控制某些类型对象的生命周期(新建/删除),以避免任何编程错误。例如,删除对象时不会向容器发出任何通知。这些对象继承自同一基类(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"派生时,任何报告更好错误消息的"标准方式">

提前谢谢。

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

我不知道它是否常见,但它看起来足够明智。它确保您的容器只能包含指向用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*,应该足够清楚。

  1. 可以使用template,但使用接收pointer to base-class的函数更好
  2. 标准C++11方式-使用带有std::is_base_of的static_assert。没有C++11-boost等价物,或者你自己的元函数

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

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

只要让它成为一个普通的函数,使用基类指针:

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

所以你的呼叫者做

container.addClass( new GreetingDerived() );

而不是

container.addClass<GreetingDerived>();