這可能是一個愚蠢的問題。
假設我們在 C 11 領域,我們使用 make_shared() 創建一個智能指標。然后我們使用這個智能指標來初始化一個變數,如下所示:
std::shared_ptr<class> = make_shared(/* args to c'tor of class*/ );
現在我知道兩件事:
- 賦值不是初始化。在這種情況下,我們進行了初始化。這意味著在上述情況下,可能會呼叫復制建構式來
shared_ptr
回傳make_shared
. - 復制省略僅在 C 17 中是強制性的。
這是否意味著在 make_shared 的每個實體上都會創建一個 shared_ptr 的臨時副本并將其插入到復制建構式中?因為這意味著為了執行緒安全,如果其他執行緒搶占執行緒并呼叫 shared_ptr::use_count() 成員函式,則必須在初始化程序中使用鎖?
uj5u.com熱心網友回復:
有兩件事可以避免復制:
- 1是編譯器的RVO(回傳值優化);
- 2 是移動建構式/賦值。
對于代碼auto foo = std::make_shared<Foo>();
RVO 將直接在堆疊上創建物件。即使我們通過 禁用 RVO -fno-elide-constructors
,移動建構式也會嘗試用作回傳的物件 frommake_shared
是一個臨時物件。
下面是一個簡單的測驗代碼。(此代碼僅顯示概念,但不適用于現實世界的 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;
}
條件1,用c 11編譯顯示:
$ g a.cc -std=c 11 ; ./a.out
constructor
Condition 2, compile with c 11/disable RVO shows:
$ g a.cc -std=c 11 -fno-elide-constructors ; ./a.out
constructor
move
move
Condition 3, compile with c 11/disable RVO/no move shows:
$ g a.cc -std=c 11 -fno-elide-constructors -DNO_MOVE ; ./a.out
constructor
copy
copy
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/448847.html