我在大学里了解到,你总是必须释放你未使用的对象,而不是你实际上是如何做的。例如,正确地构建您的代码等等。是否有关于如何在 C++ 中处理指针的一般规则?
我目前不允许使用 boost。我必须坚持使用纯 C++,因为我使用的框架禁止使用任何泛型。
我使用过嵌入式 Symbian OS,它有一个非常好的系统,完全基于开发人员的约定。
基本上,如果一个类只是使用某些东西,它就会使用一个引用。如果一个类拥有某些东西,它会使用一个指针。
这很好用,使用起来很愉快。内存问题非常罕见。
规则:
如果有人不允许规则 1,请记住,如果您获取其他人的代码、更改变量名称并删除版权声明,那么没人会注意到。除非这是一个学校项目,否则他们实际上会使用非常复杂的工具检查这种恶作剧。另见,这个问题。
我会在这里添加另一条规则:
我们发现,刚接触 C++ 的程序员,或者从 Java 等语言转过来的程序员,似乎学习了新的东西,然后在他们想要创建任何对象时痴迷地使用它,而不管上下文如何。当在函数中本地创建对象纯粹是为了做一些有用的事情时,这尤其有害。以这种方式使用 new 可能会损害性能,并且在忘记相应的删除操作时很容易引入愚蠢的内存泄漏。是的,智能指针可以帮助后者,但它不会解决性能问题(假设在幕后使用 new/delete 或等效项)。有趣的是(好吧,也许吧),我们发现在使用 Visual C++ 时,delete 往往比 new 更昂贵。
这种混淆的一部分还来自这样一个事实,即它们调用的函数可能会将指针甚至智能指针作为参数(当引用可能会更好/更清晰时)。这使他们认为他们需要“创建”一个指针(很多人似乎认为这就是 new 所做的)才能将指针传递给函数。显然,这需要一些关于如何编写 API 的规则,以使调用约定尽可能明确,这些规则通过函数原型提供的清晰注释得到加强。
在一般情况下(资源管理,其中资源不一定是内存),您需要熟悉RAII 模式。这是 C++ 开发人员最重要的信息之一。
通常,除非必须,否则应避免从堆中分配。如果必须,请对长期存在且需要在代码的不同部分之间共享的对象使用引用计数。
有时您需要动态分配对象,但它们只会在一定的时间内使用。例如,在之前的项目中,我需要创建数据库模式的复杂内存表示——基本上是对象的复杂循环图。但是,该图仅在数据库连接期间才需要,之后可以一次性释放所有节点。在这种情况下,我称之为“本地 GC 习惯用法”的一个很好的使用模式。我不确定它是否有一个“官方”名称,因为它是我只在我自己的代码和 Cocoa 中看到的(参见Apple 的 Cocoa 参考中的NSAutoreleasePool)。
简而言之,您创建了一个“收集器”对象,该对象保存指向您使用 new 分配的临时对象的指针。它通常与程序中的某个范围相关联,可以是静态范围(例如——作为实现 RAII 习语的堆栈分配对象)或动态范围(例如——与数据库连接的生命周期相关联,如我以前的项目)。当“收集器”对象被释放时,它的析构函数释放它指向的所有对象。
另外,像 DrPizza 一样,我认为不使用模板的限制太苛刻了。但是,在对 Solaris、AIX 和 HP-UX 的古老版本进行了大量开发之后(就在最近——是的,这些平台在财富 50 强中仍然存在),我可以告诉你,如果你真的关心可移植性,你应该尽可能少地使用模板。不过,将它们用于容器和智能指针应该没问题(它对我有用)。如果没有模板,我描述的技术实施起来会更加痛苦。这将要求由“收集器”管理的所有对象都派生自一个公共基类。
天,
我建议阅读 Scott Meyers 的“Effective C++”的相关部分。易于阅读,他涵盖了一些有趣的陷阱来诱捕粗心的人。
我也对缺乏模板很感兴趣。所以没有 STL 或 Boost。哇。
顺便说一句,让人们就约定达成一致是一个绝妙的主意。就像让每个人都同意 OOD 的约定一样。顺便说一句,最新版的 Effective C++ 没有第一版中关于 OOD 约定的精彩章节,这很遗憾,例如,公共虚拟继承等约定总是模拟“isa”关系。
抢
您可以从一些实现智能指针功能的基类派生所有内容(使用 ref()/unref() 方法和计数器。
在设计该基类时,@Timbo 突出显示的所有要点都很重要。