shared_ptr a = make_shared() 是否在构造函数 运行 之前创建了 shared_ptr 的副本?
Does shared_ptr a = make_shared() create a copy of the shared_ptr before constructor is run?
这可能是个愚蠢的问题。
假设我们在 C++11 领域,我们使用 make_shared() 创建一个智能指针。然后我们使用这个智能指针来初始化一个变量,如下所示:
std::shared_ptr<class> = make_shared(/* args to c'tor of class*/ );
现在我知道了两件事:
- 赋值不是初始化。在这种情况下,我们有初始化。这意味着在上述情况下,可能会为
make_shared
. 返回的 shared_ptr
调用复制构造函数
- 复制省略仅在 C++17 中是强制性的。
这是否意味着在 make_shared 的每个实例上都会创建 shared_ptr 的临时副本并将其插入到复制构造函数中?因为这意味着为了线程安全,必须在初始化过程中获取锁,以防其他线程抢占该线程并调用 shared_ptr::use_count() 成员函数?
有两件事要避免复制:
- 1为编译器的RVO(return值优化);
- 2 是着法 constructor/assignment.
代码 auto foo = std::make_shared<Foo>();
RVO 将直接在堆栈上创建对象。甚至我们通过 -fno-elide-constructors
禁用 RVO,移动构造函数将尝试用作 make_shared
中的 returned 对象是一个临时对象。
下面是一个简单的测试代码。 (此代码仅显示概念,而不是 real-world shared_ptr 实现)
#include <iostream>
template <typename T>
struct my_shared_ptr
{
T *t_{nullptr};
my_shared_ptr(T *t): t_(t) {
std::cout << "constructor" << std::endl;
};
my_shared_ptr(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
}
my_shared_ptr<T>& operator=(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
return *this;
}
#ifndef NO_MOVE
my_shared_ptr(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
}
my_shared_ptr<T>& operator=(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
return *this;
}
#endif
};
template <typename T>
my_shared_ptr<T>
my_make_shared() {
return my_shared_ptr<T>(new T);
}
struct Foo {};
int main()
{
auto foo = my_make_shared<Foo>();
return 0;
}
条件一,用c++11编译显示:
$ g++ a.cc -std=c++11 ; ./a.out
constructor
条件二,用c++11编译/禁用RVO显示:
$ g++ a.cc -std=c++11 -fno-elide-constructors ; ./a.out
constructor
move
move
条件3,用c++11/disable编译RVO/no move显示:
$ g++ a.cc -std=c++11 -fno-elide-constructors -DNO_MOVE ; ./a.out
constructor
copy
copy
这可能是个愚蠢的问题。
假设我们在 C++11 领域,我们使用 make_shared() 创建一个智能指针。然后我们使用这个智能指针来初始化一个变量,如下所示:
std::shared_ptr<class> = make_shared(/* args to c'tor of class*/ );
现在我知道了两件事:
- 赋值不是初始化。在这种情况下,我们有初始化。这意味着在上述情况下,可能会为
make_shared
. 返回的 - 复制省略仅在 C++17 中是强制性的。
shared_ptr
调用复制构造函数
这是否意味着在 make_shared 的每个实例上都会创建 shared_ptr 的临时副本并将其插入到复制构造函数中?因为这意味着为了线程安全,必须在初始化过程中获取锁,以防其他线程抢占该线程并调用 shared_ptr::use_count() 成员函数?
有两件事要避免复制:
- 1为编译器的RVO(return值优化);
- 2 是着法 constructor/assignment.
代码 auto foo = std::make_shared<Foo>();
RVO 将直接在堆栈上创建对象。甚至我们通过 -fno-elide-constructors
禁用 RVO,移动构造函数将尝试用作 make_shared
中的 returned 对象是一个临时对象。
下面是一个简单的测试代码。 (此代码仅显示概念,而不是 real-world shared_ptr 实现)
#include <iostream>
template <typename T>
struct my_shared_ptr
{
T *t_{nullptr};
my_shared_ptr(T *t): t_(t) {
std::cout << "constructor" << std::endl;
};
my_shared_ptr(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
}
my_shared_ptr<T>& operator=(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
return *this;
}
#ifndef NO_MOVE
my_shared_ptr(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
}
my_shared_ptr<T>& operator=(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
return *this;
}
#endif
};
template <typename T>
my_shared_ptr<T>
my_make_shared() {
return my_shared_ptr<T>(new T);
}
struct Foo {};
int main()
{
auto foo = my_make_shared<Foo>();
return 0;
}
条件一,用c++11编译显示:
$ g++ a.cc -std=c++11 ; ./a.out
constructor
条件二,用c++11编译/禁用RVO显示:
$ g++ a.cc -std=c++11 -fno-elide-constructors ; ./a.out
constructor
move
move
条件3,用c++11/disable编译RVO/no move显示:
$ g++ a.cc -std=c++11 -fno-elide-constructors -DNO_MOVE ; ./a.out
constructor
copy
copy