管理生命周期
Managing life cycle
我创建了一个容器,用于控制某些类型对象的生命周期(新建/删除),以避免任何编程错误。例如,删除对象时不会向容器发出任何通知。这些对象继承自同一基类(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;
}
问题:
- 使用模板来解决这个问题是一种常见的方法吗?有什么缺点吗
- 当"T"不是从"GreetingBase"派生时,任何报告更好错误消息的"标准方式">
提前谢谢。
使用模板来解决这个问题是一种常见的方法吗?
我不知道它是否常见,但它看起来足够明智。它确保您的容器只能包含指向用new
分配的对象的指针,这很好。
有什么缺点吗?
主要问题是您的容器违反了"三条规则"。它有一个隐式生成的复制构造函数和复制赋值运算符,它们只是复制每个指针;这将为您提供两个容器对象,它们的析构函数都试图删除相同的对象。
解决这个问题的最简单方法是删除这些成员函数,或者如果您一直使用2011年以前的语言版本,则将它们声明为私有函数(没有实现)。如果您需要能够复制容器,那么您需要实现它们才能安全地复制。
就我个人而言,我会使用智能指针,而不是滚动我自己的RAII容器;std::unique_ptr
(如果容器具有独占所有权),std::shared_ptr
(如果您希望共享所有权),或者std::weak_ptr
(如果您想保留对由共享指针在其他地方管理的对象的非所有权引用)。如果您停留在过去,那么unique_ptr
将不可用,但Boost提供shared_ptr
、weak_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*
,应该足够清楚。
- 可以使用
template
,但使用接收pointer to base-class
的函数更好 - 标准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>();
- 如何在共享库的整个生命周期内存储数据
- 如何理解句子的生命周期始于对e的评估
- 它解决了什么问题,对于非真空初始化,生命周期在初始化之前就开始了
- Go/C++gRPC客户端通道和存根生命周期
- 如何将"this"的生命周期移动到C++中的另一个对象中?
- 是否可以通过使用移动/交换 c++11 来延长返回的临时变量的生命周期
- 使用对象的生命周期作为设置器的安全性
- 临时人员的生命周期传递给函数
- 我想知道在构造函数中初始化变量时的生命周期
- Lua 用户数据生命周期管理
- 如何使用 epoll(void* event.data.ptr) 管理 Connection 的生命周期
- C++引用的生命周期
- 在堆栈上有一个对象,而不是在函数的整个生命周期内
- 在 Boost ASIO 服务器中处理生命周期
- C++ lambda 生命周期
- 使用互斥锁跟踪另一个应用的生命周期
- QSharedPointer 或 std::shared_ptr 的生命周期
- 来自async_resolve的 boost::asio::ip::tcp::resolver::iterator 的生命周期是多久?
- 如何调整属于类的唯一指针的字符数组的大小.它必须在程序的整个生命周期中保持活力
- 延长 std::tuple<int&,int> 的生命周期,方法是将其分配给 const std::tuple<int, int>&