我目前正在优化我的代码,我对std :: vector有疑问
我有一个MyClass类,已经重写了copy / move构造函数及其相应的运算符。
MyClass(const std::string& id, int x);
MyClass(const MyClass& other);
MyClass(MyClass&& other);
~MyClass();
MyClass& operator=(const MyClass& other);
MyClass& opratror*(MyClass&& other);
我创建了一个向量,并尝试了以下方法
std::vector<MyClass> vec;
MyClass a("A", 1);
vec.push_back(a); //#1
vec.emplace_back("B", 2); //#2
vec.push_back(MyClass("C", 3)); //#3
在#1中,调用了复制构造函数(我知道向量是通过值存储的,因此它会复制a) 在#2中,它保存了副本构造函数调用,仅调用构造函数 在#3中,它调用构造函数并移动构造函数
但是我发现,在向量不为空的#2,#3处,每次推回/ emplace / emplace_back都会触发现有项目的复制/销毁。
在#2中,它复制“ A”并销毁现有的“ A” 在#3中,它对“ A”和“ B”的作用相同
似乎只要数组发生变化,向量都会求助于所有项。 这是否意味着使用类向量可能会使事情效率低下? 这是使用存储指针的向量的最佳解决方案,这样在重新进行操作时就没有复制/析构函数调用,而只有指针复制了吗?
谢谢
答案 0 :(得分:6)
不是度假村,而是重新分配。合同要求向量必须像普通数组一样连续存储其值。保证连续存储的唯一方法是分配一个内存块。一旦知道了,就完成了。你不能做得更大。您所能做的就是分配一个较大的块并将所有内容复制过来,然后删除较旧的较小的内存块。这就是您所看到的。
向量通常会保留一些额外的空间,以容纳可能会添加的新元素(这样复制不会在每次push_back时发生),但是当向量较小时,最初只有一点点额外空间被保留以用于未来的增长,并且这种重新分配仍然经常发生。但是随着向量大小的增加,越来越多的额外空间被保留,并且重新分配的频率降低。
如果您事先知道要push_back
()取多少值,则可以预先使用reserve()
预先分配额外的空间,并最大程度地减少重新分配。
如果您知道要向向量添加十个值:
vec.reserve(vec.size()+10);
如果向量已经具有至少十个以上的值,则可以在不重新分配的情况下接受该值,则此操作将不执行任何操作。否则,将为向量分配足够的额外空间以容纳至少十个附加值。保证接下来的十次push_backs不会导致重新分配。
答案 1 :(得分:3)
您需要移动noexcept(false)
;否则,在许多情况下将无法安全使用。
假设在一个缓冲区中有一个向量,该向量可以容纳10个元素,我们将其大小调整为适合15个,然后移动它们中的前3个。
第4次掷球。我们应该如何将向量恢复为正常状态?
相反,C ++标准要求不要使用投掷动作,它将10个元素复制到新缓冲区中。成功后,它可以销毁旧缓冲区。
通过noexcept移动,可以毫无疑问地将10个元素移动到新缓冲区中。