【问题标题】:What do I need to know about memory in C++?关于 C++ 中的内存,我需要了解什么?
【发布时间】:2010-12-26 19:58:35
【问题描述】:

我一直在尽我最大的努力学习 C++,但我之前的培训在一个主要问题上存在不足:内存管理。我的主要语言都具有自动垃圾收集功能,因此从未真正需要跟踪所有内容。我曾尝试在线阅读 C++ 中的内存管理,但我怀疑我仍然遗漏了一些东西。

所以,这是一个多部分的问题:

  • 关于内存管理,我需要了解的最低限度是什么? (或者,我到哪里去找)?
  • 我在哪里可以获得中级和高级知识/教程/等(一旦我完成了基础知识)?

  • 更具体地说:
  • 指针和引用之间的性能差异是什么?
  • 我听说在循环中,您需要确保在循环重新迭代之前对任何新指针调用delete。它是否正确?您需要对引用做些什么吗?
  • 有哪些经典的内存泄漏示例?
  • 我需要了解以下哪些内容(以及我是否真的需要使用它们 - 如果需要,在哪里?):
    • malloc
    • free
    • calloc
    • realloc

**************************** 更新 *******************

这是为了解决评论一中对 lmgtfy 的引用(由 Ewan 撰写)。如果您开始阅读那里可用的信息,它对初学者没有用处。我认为这是一个很棒的理论,但它对这个问题既不相关也不有用。

【问题讨论】:

  • 为什么有人投票关闭?这是一个完全有效的问题
  • 同意。这是一个非常好的问题。
  • 我认为你应该把这个问题分成更多的小块,对上述所有问题的任何答案都将长达几页。
  • @Christopher 正如其他人在下面所说的那样,这不是可以在 SO 回答的问题类型。我建议你按照 Neil 的建议去做,阅读一本关于 C++ 的好书,如果有什么你不明白的地方,请回答具体问题。
  • 我忘了提到关于改变我生活的主题的最佳书籍。它与语言无关,但这本书是永恒的。我建议查看 Microsoft Press 的“代码完成”。如果微软一半的开发人员阅读这本书,那家公司的软件质量就会提高 1000%。

标签: c++ memory-management pointers reference


【解决方案1】:

你真的,真的需要读一本好书——坦率地说,没有一本是不可能学习 C++ 的。我推荐Accelerated C++,作者是 Koenig & Moo,两位 C++ 的创始人。

【讨论】:

  • +1 我认为你是 100% 正确的。有很多阅读/练习要做。
  • +1 然后直接继续阅读 Scott Meyers 的 Effective C++(然后是更有效的 C++,但至少完成第一个)
  • +1。该问题表明缺乏 SO 答案无法弥补的知识。
【解决方案2】:

内存管理

基础知识

  • 每次“使用”new 都必须与“使用”删除匹配
  • 数组 new 不同于普通的 new 并且有自己的删除

_

 int*  data1 = new int(5);
 delete data1;

 int*  data2 = new int[5];
 delete [] data2;

必须知道

  • 例外情况
  • RAII
  • 4 规则。

throwing exceptions out of a destructor
Dynamically allocating an array of objects Pattern name for create in constructor, delete in destructor (C++)

最佳实践

  • 从不实际使用 RAW 指针。
  • 始终将指针包装在智能指针中。
  • 了解不同类型的智能指针以及何时使用它们

Smart Pointers: Or who owns you baby?

高级:

  • 了解异常保证
  • 了解 throw 子句的使用

What are the principles guiding your exception handling policy?

常见的泄漏方式

基础知识

// Every new is matched by a delete.
for(int loop = 0;loop < 10;++loop)
{
    data = new int(5);
}
delete data;
// The problem is that every 'use of' new is not matched by a delete.
// Here we allocate 10 integers but only release the last one.

必须知道

class MyArray
{
    // Use RAII to manage the dynamic array in an exception safe manor.
    public:
        MyArray(int size)
          :data( new int[size])
        {}
        ~MyArray()
        {
            delete [] data;
        }
    // PROBLEM:
    // Ignored the rule of 4.
    // The compiler will generate a copy constructor and assignment operator.
    // These default compiler generated methods just copy the pointer. This will
    // lead to double deletes on the memory.

    private:
        int*   data;
};

最佳实践

// Understand what the properties of the smart pointers are:
//
std::vector<std::auto_ptr<int> >   data;

// Will not work. You can't put auto_ptr into a standard container.
// This is because it uses move semantics not copy semantics.

高级:

// Gurantee that exceptions don't screw up your object:
//
class MyArray
{
   // ... As Above: Plus
   void resize(int newSize)
   {
       delete [] data;
       data = new int[newSize];
       // What happens if this new throws (because there is not enough memory)?
       // You have deleted the old data so the old data so it points at invalid memory.
       // The exception will leave the object in a completely invalid state
   }

【讨论】:

  • 我知道三的规则,我缺少的第四是什么?
  • 普通 Con/Copy Con/Assignment Op/Destructor.
【解决方案3】:

从最简单的意义上讲,您需要了解的有关内存管理的知识是,您需要删除在堆上分配的内存。因此,当创建像MyClass *myClass = new MyClass(x); 这样的对象时,您需要在代码中使用相应的delete 来释放/删除它。这在实践中看起来很容易,但是如果没有适当的设计和使用共享指针等辅助对象,这很快就会变得混乱,尤其是在维护代码和添加功能时。例如,这是一个经典的内存泄漏:

try
{
    MyClass *myClass = new MyClass(x);

    // Do some stuff can throw an exception

    delete myClass;
}
catch(...)
{
   // Memory leak on exceptions.  Delete is never called
}

或者另一个大的内存管理问题正在调用错误的删除类型:

int* set = new int[100];
delete set;   // Incorrect - Undefined behavior
// delete [] set;  is the proper way to delete an array of pointers

帮助自己的常用方法是使用 RAII 习语。 (Resource Allocation Is Initialization)

这是一个使用 std 库来防止内存泄漏的示例:

try
{ 
    auto_ptr<MyClass> myClass(new MyClass(x));
    // Now the heap allocated memory associated with myClass
    // will automatically be destroyed when it goes out of scope,
    // but you can use it much like a regular pointer

    myClass->memberFunction();
} 
catch (...)
{

}

有关auto_ptr 的更多信息,请访问here。如果您可以使用 C++11,shared_ptr 是强烈推荐的选择,并且通常比 auto_ptr 更受欢迎。

【讨论】:

  • "删除集;//内存泄漏!"未定义的行为!
  • "删除集;//内存泄漏!"很可能不会泄漏 - 它是 UB,它可以做任何你最不期望的事情,或者工作得很好,或者破坏堆并使程序崩溃。
  • @all: 谢谢我已经澄清了我的评论
  • @sharptooth,更有理由避免它。尽管在我的经验中(从 6 到 9 的 MS C++ 编译器),它并没有崩溃。谁知道它做了什么……也许只是删除第一个元素的内存价值?
  • 请记住,shared_ptr&lt;&gt; 是上一份技术报告的一部分,因此std::tr1::shared_ptr&lt;&gt; 是完全有效的。我不知道有多少编译器支持它,但即使出于某种愚蠢的原因你不能使用 Boost 也没关系。
【解决方案4】:

首先,您应该了解stackheap 的概念。

了解这些概念后,继续学习语言结构。

【讨论】:

  • 其实你应该明白自动变量分配与动态内存分配的概念;以及对象的持续时间。
  • @Thomas,当您与同事讨论代码时,您将它们称为堆栈/堆变量或自动/动态变量。我敢打赌它是前者。不要那么迂腐。
【解决方案5】:

关于内存管理,我需要了解的最低限度是什么? (要么, 我到哪里去找)?

对于每一个新的,必须有一个删除

我在哪里可以获得中级和高级知识/教程/等(一旦我完成了基础知识)?

阅读Effective C++More Effective C++Effective STL。然后谷歌 (std::)auto_ptr、(boost::)scoped_ptr 和 (boost::)shared_ptr

更具体地说:指针和引用之间的性能差异是什么?

我不知道,因为引用是指针值的副本,我预计不会出现任何大的性能问题。

我听说在循环中,您需要确保在循环重新迭代之前对任何新指针调用 delete。这是正确的吗?

是的。

你需要对引用做些什么吗?

没有。

内存泄漏的经典例子有哪些?

int * foo() {
...
return new int(...);
}

int main() {
int i = *foo();
...
//the new int() from foo leaks
}

我需要了解以下哪些内容(以及我是否真的需要使用 他们——如果有,在哪里?):

首先,你不应该 delete 一个 malloced 指针,也不应该 free 一个用 new 创建的指针。通常,这些函数不应出现在 c++ 代码中。但是,如果您发现自己在 c-land...

malloc :类似于 new(在堆上分配内存)
free :类似于 delete(堆上的空闲内存)
calloc :类似于 new + memset(在堆上分配内存,将其设置为零)
realloc:尝试调整内存块的大小,或创建新的内存块并复制旧数据,freeing 旧指针。没有真正的 c++ 等价物。

通过 google 可以找到一些简洁的记忆材料(是这样拼写的吗?)placement new

【讨论】:

  • 值得注意的是,您永远不要混合使用 new/free 和 malloc/delete。它们不做同样的事情,因为 new/delete 将运行构造函数和析构函数,但 malloc/free 不会。然而,new/delete 通常是使用 malloc/free 实现的,这几乎是您唯一一次在 C++ 中看到这些 C 函数的合法用法。
  • @rmeador。我会在我的回答中补充一点。
【解决方案6】:

您应该查看smart pointers,在内存管理方面,它们让您的生活变得更轻松。

【讨论】:

  • 最好先学习基础知识,而不是躲在抽象背后,当它崩溃时害怕。
  • 所以最好不要先用 GC 学习一门语言?在细节之前学习抽象很好,但很高兴知道有细节要学习
  • 那篇文章开头很好,但那个例子(在文章中)太糟糕了。
【解决方案7】:

《作为 C 和 C++ 中的编程概念的内存》一书非常适合 C/C++ 新手阅读。

【解决方案8】:

从您的列表中,您错过了newdelete - 有人说永远不要使用mallocfree

也是经常被遗忘的delete[]

【讨论】:

  • 这份清单是他完全正确地怀疑他可能不需要知道的事情。
【解决方案9】:

哇,要解决的问题很多。

最重要的是始终如一地勤奋和自律。这适用于任何语言的任何资源,甚至更安全的托管语言。人们觉得当一种语言为他们管理他们的记忆时,他们不必考虑它。但最好在完成资源后尽快释放它们。总觉得“垃圾回收”这几年让程序员偷懒了。

当我在像 c++ 这样的语言中分配内存时,我会确保在使用它之前先释放它。换句话说,我写了分配,然后解除分配,然后在中间填充。养成一致的习惯很重要。我认为这是最起码的学习……适当和有纪律的资源管理。这不仅仅是关于内存,它应该应用于所有资源,包括数据库引用、文件引用、上下文句柄和其他此类动物。

C++ 中内存管理的整个主题相当广泛。我会说尽可能多地阅读、学习和编码。

示例:

char* myusedmemory;

myusedmemory = (char *)malloc(1000);  // allocate memory

free(myusedmemory);  //  immediately deallocate memory

/*  go back and fill in the code between */

有很多很好的参考资料可以获取有关该主题的更多知识。我发现浏览 relisoft.com 上的教程对我很有帮助,尽管那里的主要教程是特定于 Windows 的。 Another good reference can be found here.

就指针和引用之间的区别而言,主要区别之一是灵活性。您必须立即定义引用( int iExample; int& refExample = iExample; )我认为不会有太大的性能差异。然而,更强大和更灵活的指针将更加危险,并且需要上述纪律来管理。

examples of memory leaks are here。但您可以通过谷歌搜索“C++ 中的内存泄漏”找到更多信息

mallocfreecallocrealloc 而言,这些只是与任何其他命令一样的函数,在这些特殊情况下,是包含在标准库中的函数。您应该了解它们的作用以及如何使用它们,就像使用任何其他函数一样,就像常见的 printf() 一样。

请注意:Smart pointers 是一种非常好的方式,而且通常更安全。

另外,我想提一下Code Complete,这是我读过的关于资源管理主题的最佳书籍。我已经读过很多次了。

【讨论】:

    【解决方案10】:

    在其他语言中,您已经必须使用“finally”(Java 中)或“using”(C# 中)等机制来跟踪数据库连接、窗口句柄、套接字等。在 C++ 中,只需将内存添加到该列表。这在概念上并没有什么不同。

    【讨论】:

      【解决方案11】:

      这里有一些经常引起学生注意的事情:大的、非常大的对象,例如数组,应该分配在动态内存中(即使用new)。

      另外,不要绕过大型物体。将指针(最好是智能指针)传递给对象。复制大对象会消耗大量处理器时间。

      设置并记录有关对象分配和所有权的规则。被调用者或调用者是否拥有该对象?

      不返回对本地对象的引用,也不返回指向本地对象的指针。

      【讨论】:

        【解决方案12】:

        了解 RAII。这里有人指出了这一点,但同时解释了新/删除的东西,没有强调 RAII 的重要性。使用 RAII,您不必担心内存管理。

        刚接触 C++ 的人倾向于像在 Java 中一样将“新”放在任何地方进行编码。这在很多情况下是一个坏习惯,在某些情况下你无法避免(但从我的项目经验来看,这很可能永远不会)。

        只是添加这个评论来强调它;)但是所有的 cmets 都是完全正确的。

        【讨论】:

          【解决方案13】:

          每个人都在提到 new 和 delete,但大多数时候,你不需要也不应该明确地使用它们:

          • 最好的办法是使用标准容器并让其进行内存管理。
          • 如果无法做到这一点,请使用带有引用计数的智能指针,或者在最后采用更智能的垃圾收集。

          当然,出于性能原因,您可能希望在性能关键的情况下偶尔使用新的和删除的对,但这应该是例外而不是规则。

          【讨论】:

            【解决方案14】:

            newdelete 是内存管理最重要的两个关键字。在最简单的情况下,您只需要记住为您调用new 的每个对象调用delete。因此,如果您在循环中调用new,则需要确保在每个new ed 对象上调用delete。只要您将每个指针的副本保存在以后可以删除的位置,您就不需要在循环中执行此操作。

            mallocfreecallocrealloc 都可能比您需要担心的更先进。如果标准的new/delete 感觉受到限制,我想只要记住它们就在那里。

            总而言之,智能指针可以提供很大帮助,但有时在处理智能指针之前知道如何做一些困难的事情会很有帮助。

            【讨论】:

            • "malloc et al are advanced":你肯定是在开玩笑。
            • 不,对于 C++,它们仅在您编写替换 operator new 时才重要。这绝对是先进的。
            • @MSalters:同意。原来的帖子是询问 c++ 的。对于 c++,我认为new/delete 是事实上的内存分配方法。使用“malloc”等的唯一原因是当您尝试做超出new/delete 能力的事情时
            • 它们根本不重要。即使是替换运算符 new 也可以调用 global ::operator new(size_t) 来请求内存块。您永远不必触摸 malloc()。
            • 如果您出于某种原因查看非常古老的 C 代码,它们确实很重要。还有很多。
            【解决方案15】:

            在学习 C++ 的过程中,我发现使用像 Valgrind 这样的内存分析工具对于帮助查找内存泄漏是必不可少的。当您从 Valgrind 运行程序(使用调试符号编译)时,它将识别内存被分配但以后永远不会释放的行。

            我使用这些命令行参数:

             valgrind --leak-check=yes --num-callers=8 ./myExecutable
            

            请注意,您的程序运行速度会比单独运行时慢得多,但通常值得付出努力。

            【讨论】:

              猜你喜欢
              • 2011-04-15
              • 1970-01-01
              • 2011-01-13
              • 1970-01-01
              • 1970-01-01
              • 2011-04-12
              • 1970-01-01
              • 1970-01-01
              • 2016-02-14
              相关资源
              最近更新 更多