简体   繁体   English

C ++内存管理

[英]C++ Memory management

I've learned in College that you always have to free your unused Objects but not how you actually do it. 我在大学里学到,你总是要释放你未使用过的物品,而不是你实际上是如何做到的。 For example structuring your code right and so on. 例如,正确构建代码等等。 Are there any general rules on how to handle pointers in C++? 关于如何在C ++中处理指针有什么一般规则吗?

I'm currently not allowed to use boost. 我目前不允许使用提升。 I have to stick to pure c++ because the framework I'm using forbids any use of generics. 我必须坚持使用纯c ++,因为我使用的框架禁止使用泛型。

I have worked with the embedded Symbian OS, which had an excellent system in place for this, based entirely on developer conventions. 我使用嵌入式Symbian操作系统,它有一个完美的系统,完全基于开发人员惯例。

  1. Only one object will ever own a pointer. 只有一个对象拥有指针。 By default this is the creator. 默认情况下,这是创建者。
  2. Ownership can be passed on. 所有权可以传递。 To indicate passing of ownership, the object is passed as a pointer in the method signature (eg void Foo(Bar *zonk);). 为了表明所有权的传递,该对象作为方法签名中的指针传递(例如void Foo(Bar * zonk);)。
  3. The owner will decide when to delete the object. 所有者将决定何时删除该对象。
  4. To pass an object to a method just for use, the object is passed as a reference in the method signature (eg void Foo(Bat &zonk);). 要将对象传递给仅供使用的方法,该对象将作为方法签名中的引用传递(例如void Foo(Bat&zonk);)。
  5. Non-owner classes may store references (never pointers) to objects they are given only when they can be certain that the owner will not destroy it during use. 非所有者类可以存储对它们所给对象的引用(从不指针),只有当它们可以确定所有者在使用期间不会销毁它时。

Basically, if a class simply uses something, it uses a reference. 基本上,如果一个类只使用某个东西,它会使用一个引用。 If a class owns something, it uses a pointer. 如果一个类拥有某个东西,它会使用一个指针。

This worked beautifully and was a pleasure to use. 这很好用,很愉快。 Memory issues were very rare. 记忆问题非常罕见。

Rules: 规则:

  1. Wherever possible, use a smart pointer . 尽可能使用智能指针 Boost has some good ones . Boost有一些好的
  2. If you can't use a smart pointer, null out your pointer after deleting it . 如果您不能使用智能指针, 请在删除指针后将其清空
  3. Never work anywhere that won't let you use rule 1. 永远不要在任何不允许您使用规则1的地方工作。

If someone disallows rule 1, remember that if you grab someone else's code, change the variable names and delete the copyright notices, no-one will ever notice. 如果某人不允许遵守规则1,请记住,如果您抓住其他人的代码,更改变量名称并删除版权声明,任何人都不会注意到。 Unless it's a school project, where they actually check for that kind of shenanigans with quite sophisticated tools. 除非是学校项目,否则他们会用相当复杂的工具检查那种恶作剧。 See also, this question . 另见这个问题

I would add another rule here: 我会在这里添加另一个规则:

  • Don't new/delete an object when an automatic object will do just fine. 当自动对象完成时,不要新建/删除对象。

We have found that programmers who are new to C++, or programmers coming over from languages like Java, seem to learn about new and then obsessively use it whenever they want to create any object, regardless of the context. 我们发现,不熟悉C ++的程序员,或者来自Java等语言的程序员,似乎都会学习新的东西,然后无论何时想要创建任何对象,都会使用它。 This is especially pernicious when an object is created locally within a function purely to do something useful. 当一个对象在函数内部本地创建纯粹是为了做一些有用的事情时,这尤其有害。 Using new in this way can be detrimental to performance and can make it all too easy to introduce silly memory leaks when the corresponding delete is forgotten. 以这种方式使用new可能会对性能产生不利影响,并且当忘记相应的删除时,很容易引入愚蠢的内存泄漏。 Yes, smart pointers can help with the latter but it won't solve the performance issues (assuming that new/delete or an equivalent is used behind the scenes). 是的,智能指针可以帮助后者,但它不会解决性能问题(假设在幕后使用new / delete或等效项)。 Interestingly (well, maybe), we have found that delete often tends to be more expensive than new when using Visual C++. 有趣的是(好吧,也许),我们发现使用Visual C ++时删除通常比新的更昂贵。

Some of this confusion also comes from the fact that functions they call might take pointers, or even smart pointers, as arguments (when references would perhaps be better/clearer). 其中一些混淆也来自于它们调用的函数可能会将指针,甚至智能指针作为参数(当引用可能更好/更清晰时)。 This makes them think that they need to "create" a pointer (a lot of people seem to think that this is what new does) to be able to pass a pointer to a function. 这使得他们认为他们需要“创建”一个指针(许多人似乎认为这是新的做法)才能将指针传递给函数。 Clearly, this requires some rules about how APIs are written to make calling conventions as unambiguous as possible, which are reinforced with clear comments supplied with the function prototype. 显然,这需要一些关于如何编写API以使调用约定尽可能明确的规则,这些规则通过函数原型提供的清晰注释得到加强。

In the general case (resource management, where resource is not necessarily memory), you need to be familiar with the RAII pattern . 在一般情况下(资源管理,资源不一定是内存),您需要熟悉RAII模式 This is one of the most important pieces of information for C++ developers. 这是C ++开发人员最重要的信息之一。

In general, avoid allocating from the heap unless you have to. 通常,除非必须,否则请避免从堆中分配。 If you have to, use reference counting for objects that are long-lived and need to be shared between diverse parts of your code. 如果必须,请对长期存在且需要在代码的不同部分之间共享的对象使用引用计数。

Sometimes you need to allocate objects dynamically, but they will only be used within a certain span of time. 有时您需要动态分配对象,但它们只会在一定的时间内使用。 For example, in a previous project I needed to create a complex in-memory representation of a database schema -- basically a complex cyclic graph of objects. 例如,在之前的项目中,我需要创建数据库模式的复杂内存表示 - 基本上是对象的复杂循环图。 However, the graph was only needed for the duration of a database connection, after which all the nodes could be freed in one shot. 但是,图表仅在数据库连接的持续时间内需要,之后可以一次性释放所有节点。 In this kind of scenario, a good pattern to use is something I call the "local GC idiom." 在这种情况下,使用的好模式是我称之为“本地GC惯用语”。 I'm not sure if it has an "official" name, as it's something I've only seen in my own code, and in Cocoa (see NSAutoreleasePool in Apple's Cocoa reference). 我不确定它是否具有“官方”名称,因为它是我在我自己的代码和Cocoa中看到的(参见Apple的Cocoa参考中的NSAutoreleasePool )。

In a nutshell, you create a "collector" object that keeps pointers to the temporary objects that you allocate using new. 简而言之,您创建了一个“收集器”对象,该对象保持指向您使用new分配的临时对象的指针。 It is usually tied to some scope in your program, either a static scope (eg -- as a stack-allocated object that implements the RAII idiom) or a dynamic one (eg -- tied to the lifetime of a database connection, as in my previous project). 它通常与程序中的某个范围相关联,或者是静态范围(例如 - 作为实现RAII惯用语的堆栈分配对象)或动态范围(例如 - 与数据库连接的生命周期相关联,如我以前的项目)。 When the "collector" object is freed, its destructor frees all of the objects that it points to. 当释放“collector”对象时,它的析构函数释放它指向的所有对象。

Also, like DrPizza I think the restriction to not use templates is too harsh. 另外,像DrPizza一样,我认为不使用模板的限制太严厉了。 However, having done a lot of development on ancient versions of Solaris, AIX, and HP-UX (just recently - yes, these platforms are still alive in the Fortune 50), I can tell you that if you really care about portability, you should use templates as little as possible. 但是,在古老版本的Solaris,AIX和HP-UX上进行了大量的开发(最近 - 是的,这些平台在财富50强中仍然存在),我可以告诉你,如果你真的关心可移植性,那么你应该尽可能少地使用模板。 Using them for containers and smart pointers ought to be ok, though (it worked for me). 使用它们作为容器和智能指针应该没问题(虽然它对我有用)。 Without templates the technique I described is more painful to implement. 如果没有模板,我所描述的技术实施起来会更加痛苦。 It would require that all objects managed by the "collector" derive from a common base class. 它要求“收集器”管理的所有对象都来自公共基类。

G'day, 天儿真好,

I'd suggest reading the relevant sections of "Effective C++" by Scott Meyers. 我建议阅读Scott Meyers的“Effective C ++”相关章节。 Easy to read and he covers some interesting gotchas to trap the unwary. 易于阅读,他涵盖了一些有趣的陷阱,陷阱不小心。

I'm also intrigued by the lack of templates. 我也对缺乏模板感兴趣。 So no STL or Boost. 所以没有STL或Boost。 Wow. 哇。

BTW Getting people to agree on conventions is an excellent idea. BTW让人们就约定达成一致是一个很好的主意。 As is getting everyone to agree on conventions for OOD. 正如让每个人都同意OOD的惯例一样。 BTW The latest edition of Effective C++ doesn't have the excellent chapter about OOD conventions that the first edition had which is a pity, eg conventions such as public virtual inheritance always models an "isa" relationship. BTW最新版的Effective C ++没有关于OOD约定的优秀章节,第一版很遗憾,例如公共虚拟继承等约定总是模仿“isa”关系。

Rob

  • When you have to use manage memory manually, make sure you call delete in the same scope/function/class/module, which ever applies first, eg: 当您必须手动使用管理内存时,请确保在相同的范围/功能/类/模块中调用delete,这首先适用,例如:
  • Let the caller of a function allocate the memory that is filled by it, do not return new'ed pointers. 让函数的调用者分配由它填充的内存,不要返回新的指针。
  • Always call delete in the same exe/dll as you called new in, because otherwise you may have problems with heap corruptions (different incompatible runtime libraries). 始终在调用new in的同一个exe / dll中调用delete,否则可能会遇到堆损坏问题(不同的不兼容的运行时库)。

you could derive everything from some base class that implement smart pointer like functionality (using ref()/unref() methods and a counter. 你可以从一些基类实现智能指针之类的功能(使用ref()/ unref()方法和计数器)。

All points highlighted by @Timbo are important when designing that base class. @Timbo突出显示的所有要点在设计基类时都很重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM