Должен ли я использовать С++ 11 emplace_back с указателями?
Имея обычную базовую → Производную иерархию, например:
class Fruit { ... };
class Pear : Fruit { ... };
class Tomato : Fruit { ... };
std::vector<Fruit*> m_fruits;
Имеет ли смысл (например, имеет более высокую производительность) использовать emplace_back вместо push_back?
std::vector::emplace_back( new Pear() );
std::vector::emplace_back( new Tomato() );
Ответы
Ответ 1
Указатели являются скалярными типами и, следовательно, литеральными типами, поэтому копирование, перемещение и создание emplace (из lvalue или rvalue) эквивалентны и обычно скомпилируются в идентичный код (скалярная копия). push_back
более понятно, что вы выполняете скалярную копию, тогда как emplace_back
следует зарезервировать для конструкции emplace, вызывающей конструктор non-copy- или move-constructor (например, конструктор преобразования или multi-argument).
Если ваш вектор должен содержать std::unique_ptr<Fruit>
вместо необработанных указателей (чтобы предотвратить утечку памяти), тогда, поскольку вы вызываете конструктор преобразования emplace_back
, будет более правильным. Однако это может все еще протекать, если расширение вектора не удалось, поэтому в этом случае вы должны использовать push_back(make_unique<Pear>())
и т.д.
Ответ 2
Не используйте необработанные указатели, используйте std::unique_ptr
следующим образом:
std::vector<std::unique_ptr<Fruit>> m_fruits;
И поскольку вы не можете скопировать конструкцию std::unique_ptr
, вы должны использовать emplace_back
(хотя вы можете использовать push_back
с std::move
).
m_fruits.emplace_back(new Pear());
m_fruits.emplace_back(new Tomato());
Edit:
Похоже, что использование std::vector<std::unique_ptr<T>>::emplace_back
и new
может протекать, если требуется std::vector
и не удается перераспределить память, мой рекомендуемый подход (до тех пор, пока С++ 14 не вводит std::make_unique
), должен использовать push_back
как это:
m_fruits.push_back(std::unique_ptr<Fruit>(new Pear));
m_fruits.push_back(std::unique_ptr<Fruit>(new Tomato));
Или используя std::make_unique
:
m_fruits.push_back(std::make_unique<Pear>());
m_fruits.push_back(std::make_unique<Tomato>());