emplace 在内存放置方面是如何工作的?
这是来自 cppreference.com 的 std::vector 的 emplace 定义:
在 pos 之前的容器中插入一个新元素。该元素是内置的,即 不要复制或移动操作。使用 std::forward(args)... 参数调用元素构造函数。元素类型必须是 EmplaceConstructible、MoveInsertable 和 MoveAssignable。
如果新的 size() 大于 capacity(),则所有迭代器和指针都无效。否则,只有迭代器和指向插入元素之后的元素的指针变得无效。
迭代器和指针保持正常工作。
选项
pos - 将在其之前构造新元素的迭代器
args - 传递给元素构造函数的参数
据我了解,向量是内存中的一个连续缓冲区(分配区域),我们可以从中获取大小和容量值。
Size - 返回容器中元素的数量,以及 capacity - 总保留空间。
问题的实质:
如果向量本质上是连续的,那么我们如何在位置 pos 处创建一个新元素 - 而不破坏向量,或者更糟糕的是,不创建新的 vector.size +1 并将所有元素复制到其中 + 一个新元素?
也就是说,如果在与 size 对应的位置执行 emplace 并且如果 size 小于容量,那么我们只需将参数传递给构造函数并创建一个元素。否则,如果我们在某个位置插入(创建)一个对象,例如,对应于向量的中间,据我所知,以下选项是可能的:
插入元素位置之后的所有元素都移动+1并插入到我们需要的位置。
但毕竟,我们必须首先移动向量的元素,然后才能在释放的空间中创建一个新的,并使用与向量类型相对应的元素 - 一切都很清楚,我们知道偏移量对象需要移动,但是派生类型的对象呢?
这是一个小例子来说明我的意思:
/ // ConsoleApplication1.cpp : Этот файл содержит функцию "main". Здесь начинается и заканчивается выполнение программы.
//
#include "pch.h"
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
int x, y;
Base() = default;
virtual ~Base() = default;
};
class Derived :public Base
{
float z;
public:
Derived() = default;
virtual ~Derived() = default;
};
class Base1
{
public:
virtual void F1()
{
}
};
class Derived1 :public Base1
{
void F1() override
{
}
};
int main()
{
vector<Base> v(10);
v.emplace(v.begin() + 2, Derived{});
vector<Base>::iterator l = v.begin();
int a = distance(v.begin(), l + 2);
v1.at(a).x;
v1.at(a).y;
v1.at(a).z;//Невозможно – усечене объекта
////
vector<Base1> v1(10);
v1.emplace(v1.begin() + 1, Derived1{});
v1.at(a).F1();//усечения быть не должно, т.к может понадобиться реализация из производного класса
}
问题涉及虚拟方法,在这种情况下,如何在对象构建之前为对象分配内存,因为 vpt 导致对象的大小无法准确知道?
也就是说,在这种情况下会发生什么:
v1.at(a).F1();//усечения быть не должно, т.к может понадобиться реализация из производного класса
也就是说,我的意思是,在创建对象之前,我们无法确定对象的大小,而且我们无法创建它——因为根据定义,emplace 是在适当的位置进行的,而不是在单独的内存区域中的某个地方,但我们也无法创建“现场”,因为我们不知道移位元素的偏移量。
这个问题源于错误的假设,即将
vector<Base>
类对象Derived
插入容器实际上会将类对象插入容器Derived
。实际上,将插入该类的一个对象Base
,因为向量是同质容器,即 只能包含一个类的对象。