Как работает emplace с точки зрения размещения в памяти ?
Вот определение emplace для std::vector с cppreference.com:
Вставляет новый элемент в контейнере непосредственно перед pos. Элемент построен на месте, т.е. не копировать или перемещать операции. Конструктор элемента вызывается с std::forward(args)...аргументы. Тип элемента должно быть EmplaceConstructible, MoveInsertable and MoveAssignable.
Если новый size() больше, чем capacity(), все итераторы и указатели становятся нерабочими. В противном случае, нерабочими становятся только итераторы и указатели на элементы, идущие после вставленных.
Итераторы и указатели остаются в рабочем состоянии.
Параметры
pos — Итератор, перед которой новый элемент будет построен
args — Аргументы направить в конструкторе элемента Оригинал:
Как я понимаю, vector – это непрерывный буфер(выделенный участок) в памяти, у которого мы можем получить значения size и capacity.
Size – возвращает количество элементов в контейнере, а capacity – общее, зарезервированное место.
Суть вопроса:
если vector – непрерывен по своей сути, то как мы можем создать новый элемент прямо на месте по позиции pos – не разрывая вектор или что еще хуже, не создавая новый vector.size +1 и не копируя все элементы в него+ новый?
Т.е если emplace производится по позиции соответствующей size и если size меньше, чем capacity, то мы просто передадим аргументы в конструктор и создадим элемент. В противном случае,если мы вставляем(создаем) объект в позиции, например соответствующей середине вектора, как я понимаю, возможен следующий вариант:
Все элементы после позиции вставляемого сдвигаем на +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
, так как вектор является гомогенным контейнером, т.е. может содержать объекты только одного класса.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Столкнулся с проблемойМне необходимо через одинаковые промежутки времени вызвать одну и туже функцию