如何在没有智能指针的情况下防止双重删除?
How to prevent double delete without smart pointers?
也许这是一个愚蠢的问题,但我只是想确保不要搞砸了。假设我有这个结构:
struct Foo{
struct Bar { virtual int calc(int x) = 0; };
Bar* barX;
Bar* barY;
int x,y;
Foo(Bar* bx,Bar* by) : barX(by),barY(by) {}
void process(int xx,int yy){
x = barX->calc(xx);
y = barY->calc(yy);
}
~Foo();
};
struct IDBar : Foo::Bar { int calc(int x) { return x; } };
int main() {
IDBar* b = new IDBar();
Foo f = Foo(b,b);
Foo f2 = Foo(new IDBar(),new IDBar());
}
我不能使用 C++11(即没有智能指针),而且我不是 100% 确定...这是删除两个(或可能只有一个)Bar
个对象:
Foo::~Foo(){
if (barX == barY){ delete barX; }
else { delete barX; delete barY; }
}
?
PS:想法是 Foo
拥有 Bar
对象(因此将负责删除它们)。传递给构造函数的 Bar
不应用于其他任何用途。实际上一个 Bar
只能属于一个 Foo
(我后来才意识到的一个缺陷,但现在还好)。此外, Foo
不应该被复制(也许我应该明确地阻止它)。
不不不不。上帝不
Foo::~Foo(){
if (barX == barY){ delete barX; }
else { delete barX; delete barY; }
}
删除对象不是自己分配的内存是非常危险的。拥有这些对象的任何人都应该删除它们。在你的例子中,main 应该负责删除那些分配的对象。
int main() {
IDBar* b = new IDBar();
IDBar* b2 = new IDBar();
IDBar* b3 = new IDBar();
Foo f = Foo(b,b);
Foo f2 = Foo(b2,b3);
delete b;
delete b2;
delete b3;
}
一个处理内存管理的对象本身的完美例子是链表。 LinkedList 在其自身内部创建节点。用户不知道它们,而只是插入 T。LinkedList 将在删除时删除它们。用户只负责删除 LinkedList,因为他或她创建了它。
std::list<MyObject*> list;
MyObject* object = new MyObject;
list.push_back(object);
//The list isn't going to delete object because it doesn't own it
delete object;
如果你坚持自己的施工,不能遵守Taztingo的设计规则;那么答案是肯定的:这是正确的方法!
然后为 class Foo 及其构造函数创建一个强大的 API 文档,接管对象 Bar 的生命周期责任。
我认为比较远非完美 - 想象一下你有 三个 指针。
为了确保不会出现双重 delete
,您只调用一个 delete
怎么样?考虑场景:
- 分配一个内存池(假设足以分配一千个对象)。这应该通过常规
new
调用在堆上分配,位于新的 PoolAllocator<T>
class 和 delete
其 desctructor 中的某处。
- 实施池,使其支持功能
void* getMemory<T>()
返回内存供 T
使用,并推进内部指针。
- 确保调用
T
的任何内容都只能通过在提供的内存池上放置 new 来创建。
最后,您不必担心内存 - 池对象可以是一个常规的堆栈变量,在堆上有内部结构。
否则,是什么禁止您实现自己的原型智能指针?
我对 Taztingo 有一点不同意见。对象获得传递给它的资源的所有权并不是不合理的。这发生在各种现实世界的代码中。 class 获取传递给构造函数的资源的所有权这一事实已记录在案,仅此而已。如果有人在未查阅文档的情况下不当使用 class,那他们就是搬起石头砸自己的脚。
然而,这确实容易出错,我们希望捕获此类错误或防止它们发生。如果有某种方法可以保证在 Foo
拥有资源时没有其他人认为他们拥有资源,我们就会继续努力。在 C++11 中,使用 std::unique_ptr
很容易。在早期的 C++ 标准中,有 std::auto_ptr
。虽然早期的 C++ 标准没有移动构造函数,但 std::auto_ptr
通过在分配或复制到新的 std::auto_ptr
.
时放弃旧 std::auto_ptr
的资源来实现类似的功能
我推荐你使用std::auto_ptr
。
如果明确允许为 class Foo
的两个构造函数参数传入相同资源的情况,请创建一个仅接受一个 std::auto_ptr<Bar>
的新构造函数。
最后,如果你想让Foo
不能被复制,只需将复制构造函数和赋值运算符都声明为私有即可。
也许这是一个愚蠢的问题,但我只是想确保不要搞砸了。假设我有这个结构:
struct Foo{
struct Bar { virtual int calc(int x) = 0; };
Bar* barX;
Bar* barY;
int x,y;
Foo(Bar* bx,Bar* by) : barX(by),barY(by) {}
void process(int xx,int yy){
x = barX->calc(xx);
y = barY->calc(yy);
}
~Foo();
};
struct IDBar : Foo::Bar { int calc(int x) { return x; } };
int main() {
IDBar* b = new IDBar();
Foo f = Foo(b,b);
Foo f2 = Foo(new IDBar(),new IDBar());
}
我不能使用 C++11(即没有智能指针),而且我不是 100% 确定...这是删除两个(或可能只有一个)Bar
个对象:
Foo::~Foo(){
if (barX == barY){ delete barX; }
else { delete barX; delete barY; }
}
?
PS:想法是 Foo
拥有 Bar
对象(因此将负责删除它们)。传递给构造函数的 Bar
不应用于其他任何用途。实际上一个 Bar
只能属于一个 Foo
(我后来才意识到的一个缺陷,但现在还好)。此外, Foo
不应该被复制(也许我应该明确地阻止它)。
不不不不。上帝不
Foo::~Foo(){
if (barX == barY){ delete barX; }
else { delete barX; delete barY; }
}
删除对象不是自己分配的内存是非常危险的。拥有这些对象的任何人都应该删除它们。在你的例子中,main 应该负责删除那些分配的对象。
int main() {
IDBar* b = new IDBar();
IDBar* b2 = new IDBar();
IDBar* b3 = new IDBar();
Foo f = Foo(b,b);
Foo f2 = Foo(b2,b3);
delete b;
delete b2;
delete b3;
}
一个处理内存管理的对象本身的完美例子是链表。 LinkedList 在其自身内部创建节点。用户不知道它们,而只是插入 T。LinkedList 将在删除时删除它们。用户只负责删除 LinkedList,因为他或她创建了它。
std::list<MyObject*> list;
MyObject* object = new MyObject;
list.push_back(object);
//The list isn't going to delete object because it doesn't own it
delete object;
如果你坚持自己的施工,不能遵守Taztingo的设计规则;那么答案是肯定的:这是正确的方法!
然后为 class Foo 及其构造函数创建一个强大的 API 文档,接管对象 Bar 的生命周期责任。
我认为比较远非完美 - 想象一下你有 三个 指针。
为了确保不会出现双重 delete
,您只调用一个 delete
怎么样?考虑场景:
- 分配一个内存池(假设足以分配一千个对象)。这应该通过常规
new
调用在堆上分配,位于新的PoolAllocator<T>
class 和delete
其 desctructor 中的某处。 - 实施池,使其支持功能
void* getMemory<T>()
返回内存供T
使用,并推进内部指针。 - 确保调用
T
的任何内容都只能通过在提供的内存池上放置 new 来创建。
最后,您不必担心内存 - 池对象可以是一个常规的堆栈变量,在堆上有内部结构。
否则,是什么禁止您实现自己的原型智能指针?
我对 Taztingo 有一点不同意见。对象获得传递给它的资源的所有权并不是不合理的。这发生在各种现实世界的代码中。 class 获取传递给构造函数的资源的所有权这一事实已记录在案,仅此而已。如果有人在未查阅文档的情况下不当使用 class,那他们就是搬起石头砸自己的脚。
然而,这确实容易出错,我们希望捕获此类错误或防止它们发生。如果有某种方法可以保证在 Foo
拥有资源时没有其他人认为他们拥有资源,我们就会继续努力。在 C++11 中,使用 std::unique_ptr
很容易。在早期的 C++ 标准中,有 std::auto_ptr
。虽然早期的 C++ 标准没有移动构造函数,但 std::auto_ptr
通过在分配或复制到新的 std::auto_ptr
.
std::auto_ptr
的资源来实现类似的功能
我推荐你使用std::auto_ptr
。
如果明确允许为 class Foo
的两个构造函数参数传入相同资源的情况,请创建一个仅接受一个 std::auto_ptr<Bar>
的新构造函数。
最后,如果你想让Foo
不能被复制,只需将复制构造函数和赋值运算符都声明为私有即可。