处理类 new 和删除运算符中的内存泄漏 C++
我非常喜欢在 C++ 中使用运算符 new
和 delete
,但稍后在程序代码中调用 delete
时经常会遇到问题。
例如,在以下代码中:
class Foo {
public:
string *ace;
Foo();
~Foo();
};
Foo::Foo() {
ace = new string;
}
Foo::~Foo() {
delete ace;
}
void UI::ButtonPressed() { //Happens when an event is triggered
Foo *foo = new Foo;
ui->label = ace; //Set some text on the GUI
delete foo; //Calls the destructor, deleting "ace" and removing it from the GUI window
}
我可以声明一个 new
字符串,但是当我删除
它时,它会从 GUI 表单中删除该值,因为该字符串现已被删除。
有没有办法让我稍后以某种方式删除这个分配的字符串?
我不想将其声明为全局变量,然后在程序源代码的最后一行删除它。我永远不能调用delete
,但据我所知,这是不好的,会导致内存泄漏。
I enjoy using the operators new
and delete
in C++ a lot but often have a problem calling delete
later on in the program's code.
For example, in the following code:
class Foo {
public:
string *ace;
Foo();
~Foo();
};
Foo::Foo() {
ace = new string;
}
Foo::~Foo() {
delete ace;
}
void UI::ButtonPressed() { //Happens when an event is triggered
Foo *foo = new Foo;
ui->label = ace; //Set some text on the GUI
delete foo; //Calls the destructor, deleting "ace" and removing it from the GUI window
}
I can declare a new
string but when I delete
it, it removes the value from the GUI form because that string has now been deleted.
Is there a way for me to delete this allocated string somehow later on?
I don't want to declare it as a global variable and then delete
it on the last line of the program's source code. I could just never call delete
but from what I have been taught that's bad and results in memory leaks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您应该阅读RAII 模式。它是 C++ 程序员需要了解的最重要的概念之一。
基本思想是资源(新建对象、HTTP 连接等)的生命周期与对象的生命周期相关。为了编写异常安全代码,这是必要的。
在您的情况下,UI 小部件将复制该对象并在其自己的析构函数中释放它。然后,调用代码可以立即释放其副本(在另一个析构函数中)。
You should read about the RAII pattern. It is one of the most important concepts to know for a C++ programmer.
The basic idea is that the lifetime of a resource (a new'ed object, an HTTP connection, etc.) is tied to the lifetime of an object. This is necessary in order to write exception safe code.
In your case, the UI widget would make a copy of the object and free it in its own destructor. The calling code could then free its copy right away (in another destructor).
如果您为
ace
和ui->label
使用std::string
那么您不必担心内存一旦foo
对象超出范围,foo->ace
就会被删除
。Right-Hand 参数的副本可用于
=
上的ui->label
(赋值操作)。您可以在 C++std::stringstring::operator=
的 code> 参考页。此外,通过使用智能指针可以完全避免此类问题,例如 增强库。阅读关于此主题的 stackoverflow 上的这篇精彩文章,以获得更好的理解。
If you are using
std::string
for bothace
andui->label
then you don't have to worry about the memory forfoo->ace
beingdelete
d once thefoo
object goes out of scope.A copy of the Right-Hand argument is made available to
ui->label
on an=
(assignment operation). You can read more about it on the C++std::string
reference page forstring::operator=
.Also, such problems can be avoided in full by using smart pointers, such as the ones provided by the boost library. Read this great post on stackoverflow on this subject to get a better understanding.
嗯,关于你的代码有很多话要说。有些事情已经说过了,例如,您应该使 string 成为普通成员,以便分配/解除分配问题完全消失(这是 C++ 程序的一般规则:如果您绝对必须使用动态分配,那就不要了,期间)。另外,使用适当的智能指针将为您进行内存管理(也是 C++ 中的一般规则:除非确实需要,否则不要自己管理动态分配)。
然而,让我们假设您必须使用动态分配,并且必须使用原始指针并在此处直接
new
和delete
。然后另一个重要的规则出现了(这实际上不是 C++ 特定的规则,而是通用的 OO 规则):不要将成员公开。将其设置为私有成员,并提供公共成员函数进行设置。然后,该公共成员函数可以在将指针分配给新对象之前正确删除旧对象。请注意,一旦分配了指针,除非您已将旧值存储在其他地方,否则旧值将永远丢失,并且如果到目前为止该对象尚未删除,则以后将无法删除它。您还需要考虑获取通过指针传递给您的对象的所有权是否真的是一个好主意(并分配给在析构函数中具有删除的指针成员是 - 不是很明显― 传递所有权的方式)。这使对象生命周期管理变得复杂,因为您必须记住是否将某个对象传递给所有权声明对象(如果您有始终传递给所有权声明对象的严格策略,这不是问题, 尽管)。像往常一样,智能指针在这里可能会有所帮助;但是,您可能会考虑制作传递对象的副本是否是更好的选择(对于 std::string 来说肯定是,但是,如上所述,这里最好有一个直接成员多于)。
因此,这里有一个完整的规则列表,其中较早的规则优先于较晚的规则,除非有充分的理由不使用它:
new
,仅在相应的析构函数中使用delete
。new
和delete
。 (实际上前一个规则是这个规则的一个特例,但是一个特例应该优先于一般规则。)Well, there's a lot to say of your code. Some things have already been said, e.g. that you should make string a normal member so the allocation/deallcoation issue goes away completely (that's a general rule for C++ programs: If you don't absolutely have to use dynamic allocation, then don't, period). Also, using an appropriate smart pointer would do the memory management for you (also a general rule in C++: Don't manage the dynamic allocations yourself unless you really have to).
However let's pretend that you have to use dynamic allocation, and you have to use raw pointers and direct
new
anddelete
here. Then another important rule comes in (which actually isn't a C++ specific rule, but a general OO rule): Don't make the member public. Make it a private member, and offer a public member function for setting it. That public member function then can properly delete the old object before assigning the pointer to the new one. Note that as soon as you assigned the pointer, unless you've stored the old value elsewhere, the old value is lost forever, and if the object has not been deleted up to then, you can't delete it later.You also want to consider whether it is really a good idea to take ownership of an object passed to you by pointer (and assigning to a pointer member which has a delete in the destructor is a ― not very obvious ― way to pass ownership). This complicates the object lifetime management because you have to remember whether you passed a certain object to an ownership-claiming object (this is not an issue if you have a strict policy of always passing to ownership-claiming objects, though). As usual, smart pointers may help here; however you may consider whether it is a better option to make a copy of the passed object (for
std::string
it definitely is, but then, here it's better to have a direct member anyway, as mentioned above).So here's a full list of rules, where earlier rules take precedence to later unless there's a good reason not to use it:
new
only in constructors anddelete
only in the corresponding destructor.new
anddelete
for a specific pointer in member functions of the same class. (Actually the previous rule is a special case of this one, but a special case which should be preferred to the general one.)这是一个更惯用的 C++ 程序:
如果您确实需要调用
new
,请始终使用适当的智能指针 - 编写delete
已从现代中消除C++ ;)Here's a more idiomatic C++ program:
If you really need to call
new
, always use an appropriate smart pointer -- Writingdelete
is banished from modern C++ ;)