小智 197
我完全赞同关于RAII和智能指针的所有建议,但我还想添加更高级别的提示:最容易管理的内存是你从未分配过的内存.与C#和Java等语言不同,几乎所有东西都是引用,在C++中,你应该尽可能地将对象放在堆栈上.正如我看到几个人(包括Dr Stroustrup)指出的那样,垃圾收集从未在C++中流行的主要原因是编写良好的C++首先不会产生太多垃圾.
别写了
Object* x = new Object;
Run Code Online (Sandbox Code Playgroud)
甚至
shared_ptr<Object> x(new Object);
Run Code Online (Sandbox Code Playgroud)
什么时候你可以写
Object x;
Run Code Online (Sandbox Code Playgroud)
pae*_*bal 102
这篇文章似乎是重复的,但在C++中,最基本的模式是RAII.
学习使用智能指针,包括boost,TR1,甚至是低位(但通常足够高效)auto_ptr(但你必须知道它的局限性).
RAII是C++中异常安全和资源处理的基础,没有其他模式(三明治等)会给你们两个(大多数时候,它都不会给你).
请参阅下面RAII和非RAII代码的比较:
void doSandwich()
{
T * p = new T() ;
// do something with p
delete p ; // leak if the p processing throws or return
}
void doRAIIDynamic()
{
std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
void doRAIIStatic()
{
T p ;
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
Run Code Online (Sandbox Code Playgroud)
总结一下(在Ogre Psalm33的评论之后),RAII依赖于三个概念:
这意味着在正确的C++代码中,大多数对象不会被构造new
,而是将在堆栈中声明.对于那些构造使用的new
,所有将以某种方式作用域(例如附加到智能指针).
作为开发人员,这确实非常强大,因为您不需要关心手动资源处理(如在C中所做的那样,或者对于Java中的某些对象,这些对象大量使用try
/ finally
用于该情况)...
"范围内的物体......将被破坏......无论退出",这都不完全正确.有办法欺骗RAII.任何flavor()都会绕过清理.退出(EXIT_SUCCESS)是这方面的矛盾.
- 威廉姆特尔
wilhelmtell是关于完全正确:有特殊的方法来欺骗RAII,所有通往过程突然停止.
这些都是特殊的方式,因为C++代码不会出现终止,退出等问题,或者在异常的情况下,我们确实需要一个未处理的异常来使进程崩溃并且核心转储其内存映像,而不是在清理之后.
但我们仍然必须了解这些案例,因为虽然它们很少发生,但它们仍然可以发生.
(谁调用terminate
或exit
使用随意的C++代码?...我记得在使用GLUT时必须处理这个问题:这个库是非常面向C的,只要积极地设计它就会让C++开发人员感到困难,比如不关心关于堆栈分配的数据,或者有关于永远不会从主循环返回的 "有趣"决定...我不会对此发表评论.
Dou*_* T. 25
你会想看看智能指针,比如boost的智能指针.
代替
int main()
{
Object* obj = new Object();
//...
delete obj;
}
Run Code Online (Sandbox Code Playgroud)
一旦引用计数为零,boost :: shared_ptr将自动删除:
int main()
{
boost::shared_ptr<Object> obj(new Object());
//...
// destructor destroys when reference count is zero
}
Run Code Online (Sandbox Code Playgroud)
请注意我的最后一个注释,"当引用计数为零时,这是最酷的部分.因此,如果您拥有对象的多个用户,则无需跟踪对象是否仍在使用中.一旦没有人引用您的共享指针,它被破坏了.
然而,这不是灵丹妙药.虽然您可以访问基本指针,但您不希望将其传递给第三方API,除非您对其所做的事情充满信心.很多时候,你的"发布"东西到其他一些线程,以便在创建范围完成后完成工作.这在Win32中的PostThreadMessage中很常见:
void foo()
{
boost::shared_ptr<Object> obj(new Object());
// Simplified here
PostThreadMessage(...., (LPARAM)ob.get());
// Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}
Run Code Online (Sandbox Code Playgroud)
一如既往,使用任何工具的思维上限......
Jer*_*rks 11
大多数内存泄漏是由于不清楚对象所有权和生命周期.
首先要做的是尽可能在Stack上进行分配.这涉及大多数需要为某些目的分配单个对象的情况.
如果你确实需要"新"一个对象,那么大多数时候它将在其整个生命周期中拥有一个明显的所有者.对于这种情况,我倾向于使用一堆集合模板,这些模板设计用于通过指针"拥有"存储在其中的对象.它们是使用STL向量和映射容器实现的,但有一些区别:
我对STL的关注是它专注于Value对象,而在大多数应用程序中,对象是唯一的实体,没有在这些容器中使用所需的有意义的复制语义.
Dar*_*enW 10
呸,你们年幼的孩子和你那些新奇的垃圾收集者......
关于"所有权"的非常强有力的规则 - 软件的哪个对象或部分有权删除该对象.明确的注释和明智的变量名称,以便在指针"拥有"或"只是看,不要触摸"时显而易见.为了帮助确定谁拥有什么,尽可能遵循每个子程序或方法中的"三明治"模式.
create a thing
use that thing
destroy that thing
Run Code Online (Sandbox Code Playgroud)
有时需要在广泛不同的地方创造和销毁; 我觉得很难避免这种情况.
在任何需要复杂数据结构的程序中,我使用"所有者"指针创建一个包含其他对象的严格明确的对象树.此树模拟应用程序域概念的基本层次结构.示例3D场景拥有对象,灯光,纹理.程序退出时渲染结束时,有一种清除方法可以摧毁一切.
每当一个实体需要访问另一个实体,扫描数组或其他内容时,就会根据需要定义许多其他指针; 这些都是"正在寻找".对于3D场景示例 - 对象使用纹理但不拥有; 其他对象可能使用相同的纹理.对象的破坏不会引起任何纹理的破坏.
是的,这是耗时的,但这就是我的工作.我很少有内存泄漏或其他问题.但后来我在有限的高性能科学,数据采集和图形软件领域工作.我不经常处理银行和电子商务,事件驱动的GUI或高网络异步混乱等交易.也许那些新奇的方式在那里有优势!
好问题!
如果您正在使用c ++并且您正在开发实时CPU和内存boud应用程序(如游戏),则需要编写自己的内存管理器.
我认为你能做的更好的是合并各种作者的一些有趣的作品,我可以给你一些提示:
固定大小分配器在网络中随处可见
Alexandrescu在2001年的完美着作"现代c ++设计"中介绍了小对象分配
在Dimitar Lazarov编写的游戏编程宝石7(2008)中名为"高性能堆分配器"的惊人文章中可以找到一个伟大的进步(源代码分发)
一种很大的资源列表中可以找到这个文章
不要自己开始写一个noob unuseful allocator ...首先记录你自己.
关于如何不泄漏已经有很多,但如果你需要一个工具来帮助你跟踪泄漏,请看看: