Я экспериментировал с shared_ptr
и make_shared
с С++ 11 и запрограммировал небольшой пример игрушек, чтобы увидеть, что на самом деле происходит при вызове make_shared
. В качестве инфраструктуры я использовал llvm/clang 3.0 вместе с библиотекой llvm std С++ в XCode4.
class Object
{
public:
Object(const string& str)
{
cout << "Constructor " << str << endl;
}
Object()
{
cout << "Default constructor" << endl;
}
~Object()
{
cout << "Destructor" << endl;
}
Object(const Object& rhs)
{
cout << "Copy constructor..." << endl;
}
};
void make_shared_example()
{
cout << "Create smart_ptr using make_shared..." << endl;
auto ptr_res1 = make_shared<Object>("make_shared");
cout << "Create smart_ptr using make_shared: done." << endl;
cout << "Create smart_ptr using new..." << endl;
shared_ptr<Object> ptr_res2(new Object("new"));
cout << "Create smart_ptr using new: done." << endl;
}
Теперь посмотрите на результат, пожалуйста:
Создайте smart_ptr, используя make_shared...
Конструктор make_shared
Копировать конструктор...
Копировать конструктор...
деструктор
деструктор
Создайте smart_ptr, используя make_shared: done.
Создать smart_ptr с помощью нового...
Конструктор new
Создайте smart_ptr с помощью new: done.
деструктор
деструктор
Похоже, что make_shared
вызывает конструктор копирования два раза. Если я выделяю память для Object
с помощью регулярного new
, это не произойдет, будет создан только один Object
.
Что мне интересно, так это следующее. Я слышал, что make_shared
должен быть более эффективным, чем использование new
(1, 2). Одна из причин заключается в том, что make_shared
выделяет счетчик ссылок вместе с объектом, который должен управляться в том же блоке памяти. Хорошо, я понял. Это, конечно, более эффективно, чем две отдельные операции выделения.
Напротив, я не понимаю, почему это связано с стоимостью двух вызовов конструктора копирования Object
. Из-за этого я не уверен, что make_shared
более эффективен, чем распределение, используя new
в каждом случае. Я здесь не прав? Хорошо, можно реализовать конструктор перемещения для Object
, но все же я не уверен, что это более эффективно, чем просто выделить Object
через new
. По крайней мере, не в каждом случае. Было бы правдой, если копирование Object
было бы дешевле, чем выделение памяти для контрольного счетчика. Но внутренний счетчик shared_ptr
может быть реализован с использованием пары примитивных типов данных, правда?
Можете ли вы помочь и объяснить, почему make_shared
- это способ работать с точки зрения эффективности, несмотря на наложенные на них накладные расходы?