[英]thread-safe copy assignment operator for smart pointer class
我正在實現一個智能指針類,但有一些困惑。 如果人們能幫我澄清,我將不勝感激。
1:我認為智能指針類在構造函數中應該有“new”,在析構函數中應該有“delete”。 但是我似乎找不到放置“new”的地方......所以用戶將負責創建新的,而智能指針類有助於清理它?
2:在設計復制賦值運算符時,一種流行的方法是復制 n 交換以確保線程安全。 但是,copy-n-swap 要求對象按值(而不是按引用)傳入。 它還能用於設計智能指針嗎? 我擔心這是一個指針類,因此可能無法按值傳入。 但我對此不太確定。
3:如果我在面試時被要求編寫一個智能指針,我是否必須提供某種類型的引用計數? 思想引用計數特定於 shared_ptr ...
4:這一定是一個愚蠢的問題,但最好先問,然后再把疑慮藏在心里。 對於SmartPointer<T>& sp
訪問 ptr,它應該使用sp->ptr
還是sp.ptr
??
感謝您的投入。
template <class T>
class SmartPointer{
T* ptr;
public:
SmartPointer():ptr(NULL){}
SmartPointer(const T& p):ptr(p){}
SmartPointer(const SmartPointer<T>& sp):ptr(sp->ptr){}
SmartPointer<T>& operator=(const SmartPointer<T>& sp);
~SmartPointer(){delete ptr;}
T& operator*(){return *ptr;}
T* operator->(){return ptr;}
};
SmartPointer& operator=(const SmartPointer& sp){
T* pOrig = ptr;
ptr = new T(sp.ptr);
delete pOrig;
return *this;
}
那么用戶將負責創建新的,而智能指針類有助於清理它?
是的,通常就是這樣做的。 例子:
std::unique_ptr<Foo> smart_ptr(new Foo);
構造(和分配)留給客戶端的原因之一是因為 T 可以具有各種參數化構造函數。 至少可以說這很棘手,並且如果您希望允許智能指針以所有可能的方式自行構造 T,則可能涉及帶有一些非常奇特的模板魔術的可變參數構造函數模板。 讓客戶端在構造智能指針時傳入指針更簡單。 只要他們在構造時立即這樣做,它就是安全的,因為如果operator new
在我們進入智能指針構造之前拋出異常,則不會分配/構造任何內容,因此除了調用中已經存在的內容之外,將沒有任何內容需要清理堆棧(為了異常安全,它應該符合 RAII)。
如果你想讓它對 API 邊界健壯,那么你通常希望捕獲一個“刪除器”,它會在生成代碼的同一站點調用“操作符刪除”(在跨模塊邊界嘗試釋放內存時很重要)在模塊 B 中分配在模塊 A 中的內存會產生未定義的行為)。
但是,copy-n-swap 要求對象按值(而不是按引用)傳入。 它還能用於設計智能指針嗎? 我擔心這是一個指針類,因此可能無法按值傳入。 但我對此不太確定。
對於您正在制作的這種不實現引用計數的智能指針,通常最好的設計是禁止復制(賦值和復制 ctor,盡管移動 ctor 很好)。
否則,您將回到過時的轉讓所有權的做法(如古老的std::auto_ptr
)或嘗試深度復制指針對象。
轉讓所有權特別容易出現人為錯誤,因為它將其復制的源視為可變的(這是完全不尋常且令人毛骨悚然的行為)。 如果你這樣做,你可以使用原子 CAS 來交換指針,但你需要讓復制構造函數和賦值運算符通過引用接受事物,而不是常量引用或值,因為它會將源視為可變的(要么是或使您擁有可變的私有ptr
成員並使用 const 引用)。
深度復制指針是一種有趣的想法,但其中的一個問題是有人可能會嘗試在 T 不是完整類型(未定義,僅聲明)的站點復制您的智能指針。 它類似於析構函數的問題,所以如果你想像這樣制作一個深度復制智能指針,一個強大的解決方案是捕獲 T 的復制構造函數(例如:存儲一個函數指針,指向你在智能指針時生成的函數被構造為克隆/復制構造 T 的新元素)。
還對您現有的深度復制代碼進行了輕微修復:
SmartPointer& operator=(const SmartPointer& sp){
T* pOrig = ptr;
ptr = new T(*sp.ptr); <-- need to dereference here
delete pOrig;
return *this;
}
3:這一定是個愚蠢的問題,但最好先問,然后再將疑慮藏在心里。 SmartPointer& sp 訪問ptr,應該使用sp->ptr 還是sp.ptr??
讓編譯器解決您的疑慮。 給定一個引用, SmartPointer& sp
,在這種情況下sp->ptr
將是一個編譯器錯誤,除非T
有一個名為ptr
的成員可以訪問,這可能不是你想要的。 sp->ptr
會調用重載的operator->
,而不是訪問智能指針的實際私有成員。 operator->
通常只為指針定義,因此嘗試在引用或 const 引用上使用它會調用重載的用戶定義運算符。
1:我會說構造函數是這樣的地方:
SmartPointer(T* p):ptr(p){}
然后你獲得與典型智能指針相同的語法——你通過調用SmartPointer objPtr(new Obj());
創建它SmartPointer objPtr(new Obj());
. 用戶可以隨心所欲地創建他的對象,智能指針負責在他之后清理它(因為這是智能指針旨在幫助解決的主要問題)。 請注意,將來您可能希望添加某種std::make_shared()
方法以避免在構造和提高性能時復制包裝器。
2:您必須考慮智能指針的行為方式。 請記住,當您嘗試復制原始指針時,您只會復制地址,而不是對象本身。 因此,如果您決定將 SmartPointer 視為配備垃圾收集器的普通指針,那么您可以自由地實現 copy-n-swap。 另一方面,您可以像在std::unique_ptr
那樣刪除operator=()或嘗試像在std::shared_ptr
那樣實現引用計數。
3:只有當你被明確要求這樣做時,我才會說。 通常通過智能指針,人們可以理解某種能夠自行控制原始指針生命周期的包裝器。 雖然,我敢肯定,如果他們問“請編寫一個簡單的智能指針的實現”並且您問“您希望它也處理共享所有權嗎?”,您將獲得一分:)
4:當然是sp.ptr
:) SmartPointer<T>& sp
是一個引用,所以為了訪問它的成員,你必須使用一個點.
操作員。
要使用sp->ptr
您必須擁有SmartPointer<T>* sp
參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.