帮助我使这段代码异常安全
所以我有这个库代码,请参阅...
class Thing
{
public:
class Obj
{
public:
static const int len = 16;
explicit Obj(char *str)
{
strncpy(str_, str, len);
}
virtual void operator()() = 0;
private:
char str_[len];
};
explicit Thing(vector<Obj*> &objs) : objs_(objs) {}
~Thing() {
for(vector<Obj*>::iterator i = objs_.begin(); i != objs_.end(); ++i) {
delete *i;
}
}
private:
vector<Obj*> objs_;
}
在我的客户端代码中...
class FooObj : public Thing::Obj
{
virtual void operator()() {
//do stuff
}
}
class BarObj : public Thing::Obj
{
virtual void operator()() {
//do different stuff
}
}
vector<Objs*> objs;
int nStructs = system_call(*structs);
for(int i = 0; i < nStructs; i++) {
objs.push_back(newFooObj(structs[i].str));
}
objs.push_back(newBarObj("bar1");
objs.push_back(newBarObj("bar2");
Thing thing(objs);
// thing does stuff, including call Obj::()() on the elements of the objs_ vector
我所坚持的是异常安全。 就目前情况而言,如果任何 Obj 构造函数抛出异常,或者 Thing 构造函数抛出异常,则向量中已有的 Objs 将发生泄漏。 该向量需要包含指向 Objs 的指针,因为它们是多态使用的。 而且,我需要在这里处理任何异常,因为这是从不了解异常的旧代码库调用的。
在我看来,我的选择是:
- 将客户端代码包装在一个巨大的 try 块中,并清理 catch 块中的向量。
- 将 try 块放在所有分配周围,其中的 catch 块调用公共清理函数。
- 一些我还没有想到的基于 RAII 的聪明想法。
- 平底船。 实际上,如果构造函数抛出异常,应用程序无论如何都会陷入困境,但我想更优雅地处理这个问题。
So I have this library code, see...
class Thing
{
public:
class Obj
{
public:
static const int len = 16;
explicit Obj(char *str)
{
strncpy(str_, str, len);
}
virtual void operator()() = 0;
private:
char str_[len];
};
explicit Thing(vector<Obj*> &objs) : objs_(objs) {}
~Thing() {
for(vector<Obj*>::iterator i = objs_.begin(); i != objs_.end(); ++i) {
delete *i;
}
}
private:
vector<Obj*> objs_;
}
And in my client code...
class FooObj : public Thing::Obj
{
virtual void operator()() {
//do stuff
}
}
class BarObj : public Thing::Obj
{
virtual void operator()() {
//do different stuff
}
}
vector<Objs*> objs;
int nStructs = system_call(*structs);
for(int i = 0; i < nStructs; i++) {
objs.push_back(newFooObj(structs[i].str));
}
objs.push_back(newBarObj("bar1");
objs.push_back(newBarObj("bar2");
Thing thing(objs);
// thing does stuff, including call Obj::()() on the elements of the objs_ vector
The thing I'm stuck on is exception safety. As it stands, if any of the Obj constructors throw, or the Thing constructor throws, the Objs already in the vector will leak. The vector needs to contain pointers to Objs because they're being used polymorphically. And, I need to handle any exceptions here, because this is being invoked from an older codebase that is exception-unaware.
As I see it, my options are:
- Wrap the client code in a giant try block, and clean up the vector in the catch block.
- Put try blocks around all of the allocations, the catch blocks of which call a common cleanup function.
- Some clever RAII-based idea that I haven't thought of yet.
- Punt. Realistically, if the constructors throw, the application is about to go down in flames anyway, but I'd like to handle this more gracefully.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
看看
boost::ptr_vector< /代码>
Take a look at
boost::ptr_vector
由于您的 Thing 析构函数已经知道如何清理向量,因此您已经基本实现了 RAII< /a> 解决方案。 您可以使用空向量初始化 Thing,并添加一个成员函数以通过指针将新 Objs 添加到该向量,而不是创建 Objs 向量,然后将其传递给 Thing 的构造函数。
这样,如果 Obj 的构造函数抛出异常,编译器将自动调用 Thing 的析构函数,正确销毁已分配的任何 Obj。
Thing 的构造函数变为无操作:
添加一个
push_back
成员:然后客户端中的分配代码变为:
正如另一位发帖者所建议的,向量中的智能指针类型也可以很好地工作。 只是不要使用STL的
auto_ptr
; 它不遵循正常的复制语义,因此不适合在 STL 容器中使用。 Boost 和即将推出的 C++0x 提供的shared_ptr
就可以了。Since your Thing destructor already knows how to clean up the vector, you're most of the way towards a RAII solution. Instead of creating the vector of Objs, and then passing it to Thing's constructor, you could initialize Thing with an empty vector and add a member function to add new Objs, by pointer, to the vector.
This way, if an Obj's constructor throws, the compiler will automatically invoke Thing's destructor, properly destroying any Objs that were already allocated.
Thing's constructor becomes a no-op:
Add a
push_back
member:Then the allocation code in your client becomes:
As another poster suggested, a smart pointer type inside your vector would also work well. Just don't use STL's
auto_ptr
; it doesn't follow normal copy semantics and is therefore unsuitable for use in STL containers. Theshared_ptr
provided by Boost and the forthcoming C++0x would be fine.答案 3 - 在向量中使用智能指针而不是 Obj*。 我建议 boost::shared_ptr。
Answer 3 - Use smart pointers instead of Obj* in your vectors. I'd suggest boost::shared_ptr.
Obj 向量的性能可能非常差,因为向量可能必须在调整大小时移动对象,并且必须将它们全部复制。 指向对象的指针更好。
我使用了 Pointtainers 它将满足您的需求。 这是原始代码。
Vector of Obj can be very poor for performance, since a vector could have to move object on resize and has to copy them all. Pointers to objects are better.
I've used Pointainers which will do what you need. Here is the original code.
您始终可以使用 Obj 向量,而不是指向 Obj 的指针向量。 在这种情况下,您需要确保可以安全地复制 Obj(当它包含指针时很危险)。 不过,由于您的 Obj 仅包含固定大小的 char 数组,因此它应该是安全的。
Instead of a vector of pointers to Obj you always can use a vector of Obj. In that case you need to make sure you can copy Obj safely (dangerous when it contains pointers). As your Obj contains only a fixed size char array it should be safe, though.
在不将 objs 中存储的类型更改为可以复制并管理 Obj* 本身的类型的情况下,您需要添加一个 try/catch 块来在抛出异常时清理 objs。 最简单的方法是这样做:
尽管如果没有抛出异常,您无论如何都会想要清理对象。
Without changing the type stored in objs to a type that can be copied and manage Obj* itself, you need to add a try/catch block to cleanup objs if an exception is thrown. The easiest way would be to do this:
Although you'll want to clean up objs anyway if an exception isn't thrown also.
嗯。 我真的很喜欢 Commodore Jaeger 的想法; 它巧妙地消除了这段代码给我带来的一些奇怪的味道。 我现在不愿意引入 Boost 库; 这是一个有点保守的代码库,我宁愿把它带入 21 世纪,蠕动和抱怨,而不是踢腿和尖叫。
Hmmm. I really like Commodore Jaeger's idea; it neatly clears up some of the funny smells this code was giving me. I'm reluctant to bring in the Boost libraries at this point; this is a somewhat conservative codebase, and I'd rather bring it into the 21st century squirming and complaining than kicking and screaming.