澄清何时使用" new" C ++中的关键字

时间:2015-03-09 23:40:26

标签: c++ memory-management new-operator

我正在阅读When should I use the new keyword in C++?Why should C++ programmers minimize use of 'new'?的答案。但我有点困惑。

当我想创建一个大小可能会发生很大变化的对象时,我应该使用new关键字是否正确?

或者,只要我在编译时不知道对象的大小,我就会使用new

采取以下示例:

  1. 我不知道编译时大小的std::vector, 包含用户定义的对象。一旦初始化,大小永远不会 变化。

  2. 我知道std::vector的初始大小,但其大小 整个计划可能会改变。

  3. 包含std::vector个对象的用户定义对象,其大小 一切都没有改变。其他领域的规模也没有变化,但是 编译时不知道大小。

  4. 用户定义的对象,其字段的大小可以全部改变

  5. 在我看来,我应该在案例1和2中使用new,而不是3和4.虽然在3和4中,我应该使用new为内部字段分配内存用户定义的对象。它是否正确?

    new的使用是否取决于对象大小的变化

    如果我不使用new,我会冒什么风险(例如stackoverflow?)?

    在示例3和4中,如果我不使用new,并且我传递了这个对象,那么每次通过它时我都会制作对象的副本吗?

    如果是这样,那么对象的大小就太大了#34; (例如,它有很多非常大的字段),传递对象可能会导致堆栈溢出,破坏堆栈中其他对象的数据?

    最后,在使用new时,我了解到我还必须使用delete。但是,如果许多不同的对象(让他们称之为A,B和C)可以访问相同的指针(P *),那么在A中调用delete如何影响B中的P *和C?我假设它为所有这些释放了内存,这样如果我现在在B或C中调用P-> get_something(),我会收到一个段错误。然后,为了防止内存泄漏,只要某些仍然可以访问P *,我就不应该调用delete,但只要我确定没有可以访问P *,我需要删除它,对吗?

7 个答案:

答案 0 :(得分:1)

你应该使用new并在运行时不知道对象数量时从动态内存中分配,或者对象需要在声明它的函数之外的生命周期。

动态内存中许多分配的问题是碎片和性能。

也就是说,如果对象可以重用,在初始化期间分配一次并重用它,然后在程序终止时删除对象。例如,您可以创建一个对象,使用来自User的输入数据重新加载它。

如果您知道不会超过对象的限制,请考虑创建一次对象数组并从数组中分配。这将减少碎片问题并可能提高性能。

new的性能问题是它需要找到合适的内存块。最好的情况是这是一个简单的O(1)访问。最坏的情况是,必须执行搜索。

因此,首选是避免分配动态内存。

编辑1:矢量
std::vector仅在需要扩展保留空间时执行分配。如果预先分配2个对象的大小并将push_back分配给第三个,则std::vector将重新分配空间。

目标是降低std::vector重新分配的概率。这可以通过保留比预测需要稍大的空间来完成。如果您平均需要从相机存储6帧,那么您将有std::vector预留空间8帧。这将减少重新分配的数量std::vector

同样,您可以重复使用矢量。如果要重新声明函数中的向量,则可能需要静态声明它并重用它。

答案 1 :(得分:1)

如果你调用一个应该返回项目向量的函数,你可以选择像这样返回向量:

std::vector<int> GetVecOfObjects()
{
     std::vector<int> objects;

     objects.push_back(1);
     objects.push_back(1);
     objects.push_back(2);
     objects.push_back(3);
     objects.push_back(5);

     return objects;
}

vector<int> myObjects;
myObjects = GetVecOFObjects();

旧版本的c ++必须在object放入myObjects时制作object的完整副本,然后在展开堆栈时销毁objects

这是因为在GetVecOfObjects()返回时将在堆栈上创建new。如果你开始使用包含数千个元素的向量,这可能是一个效率问题。

所以你可以更有效率,并使用指向向量的指针,并使用std::vector<int> *GetVecOfObjects() { std::vector<int> *objects = new std::vector<int>; objects.push_back(1); objects.push_back(1); objects.push_back(2); objects.push_back(3); objects.push_back(5); return objects; } vector<int> * myObjects = GetVecOFObjects(); ... delete (myObjects); 在堆上创建向量,以便在函数返回后它仍然存在:

objects

这里,所有被复制的都是向量的地址,因为myObjectsmyObjects都是指针。这很容易,因为向量的大小并不重要,你可以将它传递给各种其他函数,甚至其他线程,并且不用担心如果delete去向量,向量将不复存在超出范围(虽然它会在调用swap时停止存在。)

在c ++ 11及更新版本中,容器类都实现了移动语义&#39;使用objects方法vector::swap。因此,当您将vector<int> myObjects;作为堆栈中的向量返回时,事情会更有效率。由于您正在使用来自GetVecOfObjects()的向量替换向量(objects)的现有实例,因此它们将被交换&#39;。这两个对象交换对它们包含的数据的引用,这可能是非常少量的信息。 myObjects中的数据(在这种情况下它是空的,但它不是必须的)现在由objects处理,反之亦然。然后当堆栈被解开时,堆栈上的new被清理干净,从而摆脱了你显然不再使用的所有东西。

请注意,向量中的各个元素在堆栈中,因为如另一张海报所示,vector将使用{{1}}来创建元素。

答案 2 :(得分:1)

首先,让我请您介绍一下堆和栈内存的基础知识。 经历Why should C++ programmers minimize use of 'new'?。 然后,通过下面的C和C ++内存分配比较,您将完全清楚。
说明:

使用C ++新运算符: new是用于从堆分配内存的运算符。

  • 第一步,新运算符通过以下方式在堆上分配大小存储(sizeof类型) 呼叫接线员 new()。运算符new()通过将size_t作为参数并返回新分配的内存的指针。
  • 第二步,新运算符通过调用传递的类型/类的构造函数来初始化该内存。

正常使用新建/删除:

A* a = new A();

delete a;

使用C malloc库函数:  可以使用malloc C库调用在C中完成与上述相同的C ++变体,后者依次调用内核空间中的brk()系统调用来初始化内存,但从不调用该类的构造函数。要使用malloc替代上述c ++变体,我们可以按如下操作。

显式调用构造函数/析构函数("placement new"):

A* a = (A*)malloc(sizeof(A));
new (a) A();

a->~A();
free(a);

答案 3 :(得分:0)

for std :: vector不要使用new。 std :: vector本身在内部使用new,因为它需要增长

如果需要可变数量的向量,则需要新建向量的唯一时间。然后你可以有一个向量载体

答案 4 :(得分:0)

  
      
  1. 我不知道编译时大小的std::vector,包含用户定义的对象。初始化后,大小永远不会改变。
  2.   

您可以使用std::vector constructor(2)std::vector::resize()功能设置矢量的初始大小。

  
      
  1. 我知道std::vector的初始大小,但其大小可能会在整个计划中发生变化。
  2.   

与1.相同,并且您使用例如std::vector::push_back()添加更多元素。

  
      
  1. 包含std::vector个对象的用户定义对象,其大小不会发生变化。其他字段的大小也没有变化,但在编译时不知道大小。
  2.   

与1.相同,使用正确的构造函数属性初始化它们。

  
      
  1. 用户定义的对象,其字段可以全部更改
  2.   

与1.和2相同。

答案 5 :(得分:0)

首先,物体的大小永远不会改变。这包括std::vector个对象。现在这可能令人困惑,因为std::vector具有size()成员函数,实际上可以在其生命周期内返回不同的值。但这不是对象的大小,而是向量管理的元素数量。但是这些元素不在向量对象本身中,它们由指针引用。因此,无论有多少,它们都不会影响实际矢量对象的大小。

考虑到这一点,在处理你的案件时,我假设当你说大小时,你的意思是元素的数量,因为替代方案没有任何意义。

  

我不知道编译时大小的std::vector,   包含用户定义的对象。一旦初始化,大小永远不会   变化。

std::vector处理其元素的分配和释放。 请勿使用new

  

我知道std::vector的初始大小,但其大小可以   改变整个计划。

std::vector处理其元素的分配和释放。 请勿使用new

  

包含std :: vector对象的用户定义对象,其大小全部为   不要改变。其他领域的尺寸也不会发生变化,但尺寸也会发生变化   在编译时不知道。

答案取决于其他领域是什么。希望它们是编写良好的类,如std::vector,它们还负责分配和释放它们使用的任何内存。在这种情况下,请勿使用new 。如果他们没有很好的写作来处理他们自己的分配,那么我建议你重新考虑你的选择来使用它们。

  

用户定义的对象,其字段的大小可以全部改变

与上述相同。

答案 6 :(得分:0)

简而言之:

  • 根本不使用new

更长:

  • 了解堆和堆栈之间的区别是什么。
  • 了解智能指针,特别是std::make_uniquestd::make_shared
  • 了解std::vector内部如何运作
  • 如果您必须使用new,请不要忘记在某个时间点拨打delete