C++:将自动分配的对象添加到std::向量中

C++: Adding automatically allocated objects to a std::vector

本文关键字:添加 std 向量 对象 分配 C++      更新时间:2023-10-16

我写了以下代码:

#include <iostream>
#include <vector>
using namespace std;
class AClass
{
    public:
        int data;
        AClass() 
        { data = -333; cout << "+ Creating default " << data << endl; }
        AClass(const AClass &copy) 
        { data = copy.data; cout << "+ Creating copy of " << data << endl; }
        AClass(int d) 
        { data = d; cout << "+ Creating " << data << endl; }
        ~AClass() 
        { cout << "- Deleting " << data << endl; }
        AClass& operator = (const AClass &a)
        {  data = a.data; cout << "= Calling operator=" << endl; }
};
int main(void)
{
    vector<AClass> v;
    for (int i = 3; i--; )
        v.push_back(AClass(i));
    vector<AClass>::iterator it = v.begin();
    while (it != v.end())
        cout << it->data << endl, it++;
    return 0;
}

程序的输出是:

+ Creating 2
+ Creating copy of 2
- Deleting 2
+ Creating 1
+ Creating copy of 1
+ Creating copy of 2
- Deleting 2
- Deleting 1
+ Creating 0
+ Creating copy of 0
+ Creating copy of 2
+ Creating copy of 1
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0

然后我把班级改成:

class AClass
{
    public:
        int data;
        AClass(int d) 
        { data = d; cout << "+ Creating " << data << endl; }
        ~AClass() 
        { cout << "- Deleting " << data << endl; }
};

输出变为:

+ Creating 2
- Deleting 2
+ Creating 1
- Deleting 2
- Deleting 1
+ Creating 0
- Deleting 2
- Deleting 1
- Deleting 0
2
1
0
- Deleting 2
- Deleting 1
- Deleting 0

当添加新对象时,vector似乎正在复制现有对象,但似乎发生了许多不必要的分配/删除。为什么会这样?另外,为什么第二个版本在我没有提供复制构造函数的情况下仍然有效?

当添加新对象时,矢量似乎正在复制现有对象

当您添加一个元素时,例如使用v.push_back(AClass(i));,所做的是创建一个临时AClass对象并将其传递给push_backpush_back必须将此对象复制到容器中。

您看到复制的另一个原因是std::vector将其元素连续存储在数组中。如果底层数组中没有剩余空间,并且您试图在末尾添加另一个元素,则std::vector必须创建一个新数组,将旧数组中的元素复制到新数组中,然后在末尾插入新元素。如果不希望发生这种情况,可以在开始插入元素之前调用std::vector::reservestd::vector中保留足够的空间,也可以使用不同的序列容器,如std::deque,它不会连续存储元素。

似乎正在进行大量不必要的分配/删除

在C++程序中,对象经常被创建和销毁。请注意,在您的程序中,AClass的复制成本非常低:它的大小可能是四到八个字节,刚好足够容纳其int数据成员。

如果您的类型复制成本很高(例如,您可能有一个有数千个节点的大型树数据结构),那么是的,复制成本可能太高。在这种情况下,您可以将指向动态分配对象的智能指针存储在std::vector(例如std::vector<shared_ptr<AClass> >)中。如果您的编译器支持右值引用,并且具有可移动的标准库实现,则可以通过实现移动构造函数和移动赋值运算符并使用emplace_back而不是push_back来使昂贵的复制类型可移动。

为什么第二个版本在我没有提供复制构造函数的情况下仍然有效?

如果你没有声明一个复制构造函数,编译器会为你提供一个默认的复制构造函数。

Vector使用一个T的正则数组作为其存储——当您创建其中一个时,它必须以某种方式初始化空间,唯一的选项是默认构造函数。稍后,当您设置索引的值时,它会将其复制到该空间中。

在第二个版本中,即使您没有提供复制构造函数,也会自动为您生成一个。如果你声明了一个私有的,但没有实现它,你会看到一个编译器错误(因为你已经抑制了默认值的生成)

您的对象被复制,因为vector正在扩展其内部存储。如果您想避免复制,请提前调用vector::reserve来预分配内存。若您不提供自己的副本ctor,编译器将为您生成一个副本ctor(复制所有成员的副本)。

首先,如果你不提供副本cunstructor,它通常是由c++本身生成的,而vector正在复制所有数据,因为无论何时你要求,它都不确定你给他的变量是否有有效的值,例如,你可以使用一些局部变量并将其传递给vector,如果vector没有复制你给它的,然后你返回那个向量,将会出现一些内存冲突。当你向它添加一些新对象,它需要一个更大的数组来存储所有对象时,它会分配一个新数组,然后将所有现有数据复制到新数组中。