我正在阅读When should I use the new keyword in C++?和Why should C++ programmers minimize use of 'new'?的答案。但我有点困惑。
当我想创建一个大小可能会发生很大变化的对象时,我应该使用new
关键字是否正确?
或者,只要我在编译时不知道对象的大小,我就会使用new
?
采取以下示例:
我不知道编译时大小的std::vector
,
包含用户定义的对象。一旦初始化,大小永远不会
变化。
我知道std::vector
的初始大小,但其大小
整个计划可能会改变。
包含std::vector
个对象的用户定义对象,其大小
一切都没有改变。其他领域的规模也没有变化,但是
编译时不知道大小。
用户定义的对象,其字段的大小可以全部改变
在我看来,我应该在案例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 *,我需要删除它,对吗?
答案 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
这里,所有被复制的都是向量的地址,因为myObjects
和myObjects
都是指针。这很容易,因为向量的大小并不重要,你可以将它传递给各种其他函数,甚至其他线程,并且不用担心如果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是用于从堆分配内存的运算符。
A* a = new A();
delete a;
使用C malloc库函数: 可以使用malloc C库调用在C中完成与上述相同的C ++变体,后者依次调用内核空间中的brk()系统调用来初始化内存,但从不调用该类的构造函数。要使用malloc替代上述c ++变体,我们可以按如下操作。
A* a = (A*)malloc(sizeof(A));
new (a) A();
a->~A();
free(a);
答案 3 :(得分:0)
for std :: vector不要使用new。 std :: vector本身在内部使用new,因为它需要增长
如果需要可变数量的向量,则需要新建向量的唯一时间。然后你可以有一个向量载体
答案 4 :(得分:0)
- 我不知道编译时大小的
醇>std::vector
,包含用户定义的对象。初始化后,大小永远不会改变。
您可以使用std::vector
constructor(2)或std::vector::resize()
功能设置矢量的初始大小。
- 我知道
醇>std::vector
的初始大小,但其大小可能会在整个计划中发生变化。
与1.相同,并且您使用例如std::vector::push_back()
添加更多元素。
- 包含
醇>std::vector
个对象的用户定义对象,其大小不会发生变化。其他字段的大小也没有变化,但在编译时不知道大小。
与1.相同,使用正确的构造函数属性初始化它们。
- 用户定义的对象,其字段可以全部更改
醇>
与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_unique
和std::make_shared
std::vector
内部如何运作new
,请不要忘记在某个时间点拨打delete
。