我需要了解C ++中的内存?

时间:2009-12-15 14:01:18

标签: c++ memory-management pointers reference

我一直在尽力学习C ++,但我之前的培训在一个主要问题上是不足的:内存管理。我的主要语言都有自动垃圾收集,所以跟踪一切从来没有真正必要。我已经尝试过在线阅读C ++中的内存管理,但我对此感到怀疑,认为我很遗憾。

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

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

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

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

这是为了解释在评论一中对lmgtfy的引用(由Ewan提供)。如果您开始阅读那里可用的信息,则对初学者没用。我认为这是一个伟大的理论,但它对这个问题既不相关也不有用。

15 个答案:

答案 0 :(得分:24)

你真的,真的需要读一本好书 - 坦白地学习C ++是不可能的。我推荐Accelerated C++,作者:Koenig& Moo,C ++的两个创始人。

答案 1 :(得分:15)

内存管理

基本

  • 每次使用'new'必须与'使用'删除
  • 相匹配
  • Array 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中包装指针。
  • 了解不同类型的智能指针以及何时使用每个

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
   }

答案 2 :(得分:14)

从最简单的意义上说,您需要了解的内存管理是您需要删除在堆上分配的内存。因此,在创建像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更受欢迎。

答案 3 :(得分:5)

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

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

答案 4 :(得分:5)

  

我需要了解内存管理的最低限度是什么? (要么,   我在哪里可以找到它?

对于每个新内容,都必须有删除

  

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

阅读有效的C ++ 更有效的C ++ 有效的STL 。然后google(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 malloc指针,永远不要free使用new创建指针。通常,这些函数不应出现在c ++代码中。但是,如果你发现自己在c-land ......

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

googleing可以找到一些简洁的记忆内容(这是拼写的吗?)placement new

答案 5 :(得分:4)

你应该研究smart pointers,在内存管理方面,它们会让你的生活变得更轻松。

答案 6 :(得分:3)

“C和C ++中作为编程概念的内存”这本书非常适合那些不熟悉C / C ++的人。

答案 7 :(得分:2)

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

也常常被遗忘delete[]

答案 8 :(得分:2)

哇,这要解决很多问题。

最重要的是要始终如一地勤奋和纪律。对于任何语言的任何资源,甚至更安全的托管语言都是如此。人们认为,当一种语言为他们管理记忆时,他们不必考虑它。但是在完成它们之后,最好尽快释放任何资源。我一直觉得“垃圾收集”近年来让程序员变得懒惰。

当我用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&amp; refExample = iExample;)我不认为会有很多性能差异。但是,更强大,更灵活的指针将更加危险,并且需要上述规则来管理。

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

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

作为注释:Smart pointers是一种非常好的方式,通常更安全。

作为另一个注释,我想提及Code Complete,这是我读过的关于资源管理主题的最好的书。我已经阅读了很多次的封面。

答案 9 :(得分:2)

在其他语言中,您必须使用诸如“finally”(在Java中)或“使用”(在C#中)之类的机制来跟踪数据库连接,窗口句柄,套接字等。在C ++中,只需向该列表添加内存即可。它在概念上并没有任何不同。

答案 10 :(得分:1)

这是经常捕获学生的东西:大型的,非常大的对象,如数组,应该在动态内存中分配(即使用new)。

另外,不要传递大型物体。将指针,优选的智能指针传递给对象。复制大对象会占用大量处理器时间。

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

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

答案 11 :(得分:1)

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

对C ++不熟悉的人,往往像Java一样编写代码,随处可见“新”。在许多情况下这是一个坏习惯,在某些情况下你无法避免它(但从我的项目经验来看,这很可能永远不会)。

只需添加此评论即可强调它;)但是所有评论都是完全正确的。

答案 12 :(得分:1)

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

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

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

答案 13 :(得分:0)

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

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

所有人都说,智能指针可以提供很大的帮助,但有时候知道如何在处理智能指针之前先解决问题的方法很有帮助。

答案 14 :(得分:0)

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

我使用这些命令行参数:

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

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