Рассмотрим эту программу:
#include <map>
#include <string>
#define log magic_log_function // Please don't mind this.
//
// ADVENTURES OF PROGO THE C++ PROGRAM
//
class element;
typedef std::map<int, element> map_t;
class element {
public:
element(const std::string&);
element(const element&);
~element();
std::string name;
};
element::element(const std::string& arg)
: name(arg)
{
log("element ", arg, " constucted, ", this);
}
element::element(const element& other)
: name(other.name)
{
name += "-copy";
log("element ", name, " copied, ", this);
}
element::~element()
{
log("element ", name, " destructed, ", this);
}
int main(int argc, char **argv)
{
map_t map1; element b1("b1");
log(" > Done construction.");
log(" > Making map 1.");
map1.insert(std::pair<int, element>(1, b1));
log(" > Done making map 1.");
log(" > Before returning from main()");
}
Он создает несколько объектов в стеке и insert
с ними в std::map
контейнер, создание два дополнительные временные копии в процессе:
element b1 constucted, 0x7fff228c6c60
> Done construction.
> Making map 1.
element b1-copy copied, 0x7fff228c6ca8
element b1-copy-copy copied, 0x7fff228c6c98
element b1-copy-copy-copy copied, 0x232d0c8
element b1-copy-copy destructed, 0x7fff228c6c98
element b1-copy destructed, 0x7fff228c6ca8
> Done making map 1.
> Before returning from main()
element b1 destructed, 0x7fff228c6c60
element b1-copy-copy-copy destructed, 0x232d0c8
Мы можем избавиться от одного дополнительного вызова конструктора копирования, изменив std::pair
подпись std::pair<int, element&>
Однако второй временный объект все еще создается и сразу уничтожается:
element b1 constucted, 0x7fff0fe75390
> Done construction.
> Making map 1.
element b1-copy copied, 0x7fff0fe753c8
element b1-copy-copy copied, 0x1bc4098
element b1-copy destructed, 0x7fff0fe753c8
> Done making map 1.
> Before returning from main()
element b1 destructed, 0x7fff0fe75390
element b1-copy-copy destructed, 0x1bc4098
Есть ли способ сделать std::map
просто взять объект в стеке по ссылке и сделать его единственную внутреннюю копию?
Стандартная практика (с более старыми версиями C ++), где я был, состоит в использовании карты общих указателей.
Все еще создает копию общих указателей, но это обычно гораздо менее обременительно, чем копирование больших объектов.
Это один из многих вариантов использования, которые мотивировали C++11
«s move
функциональность, поддерживаемая множеством новых функций, в частности, ссылки на значения, а также множество новых стандартных интерфейсов библиотеки, в том числе std::map::emplace
, std::vector::emplace_back
, так далее.
Если по какой-либо причине вы еще не можете использовать C++11
вы можете, по крайней мере, утешить себя мыслью, что проблема была распознана, что решение стандартизировано и реализовано, и что, кроме того, многие из нас используют его, некоторые из нас [1] в рабочем коде. Итак, как гласит старая шутка, решение существует и это ваш звонок относительно того, когда вы его поднимаете.
Обратите внимание, что вам не нужно использовать emplace
функция-член, если ваши объекты реализуют конструкторы перемещения, что они могут даже сделать по умолчанию. Этого не произойдет, если у вас есть явные конструкторы копирования, поэтому ваш приведенный выше тест может привести к эффектам наблюдателя (и действительно, он может также подавить оптимизацию компилятора в случае POD, поэтому даже с C ++ 03 у вас может не быть проблемы, которую вы думаю, что вы делаете).
Есть множество доступных хаков, которые вроде бы избегают копий только с «незначительными» изменениями исходного кода, но ИМХО лучший подход — начать двигаться к C ++ 11. Что бы вы ни делали, попробуйте сделать это так, чтобы неизбежная миграция стала менее болезненной.
Ты можешь использовать устанавливать () :
Элемент создается на месте, то есть операции копирования или перемещения не выполняются. Конструктор типа элемента (value_type, то есть std :: pair) вызывается с точно такими же аргументами, что и для функции
Ну, если у вас нет emplace
Вы можете создать элемент в куче и передать указатели на карту:
typedef std::map<int, element*> map_t;
...
printf(" > Making pair 1.\n");
std::pair<int, element*> pair(1, new element ("b1")) ;
printf(" > Making map 1.\n");
map1.insert(pair);
но тогда у вас могут возникнуть утечки памяти, если вы не позаботитесь, когда ваша карта выйдет из области видимости …