1. 程式人生 > >[C++] 不要在建構函式裡呼叫std::shared_from_this()

[C++] 不要在建構函式裡呼叫std::shared_from_this()

前段時間在用std::shared_ptr/std::weak_ptr實現thread-safe和memory-safe的觀察者模式(Observer Pattern)的時候,除錯總給我報錯,最後排查了很久,發現是一個非常tricky的小錯誤。

最開始的版本大概是這樣子的:

class Observer;

class Subject {
public:
    void registerObserver(std::shared_ptr<Observer> o) = 0;
    void removeObserver(const std::shared_ptr<Observer>& o) = 0
; void notifyObservers() = 0; private: std::vector<std::weak_ptr<Observer>> observers; };

被觀察的Subject內部使用容器來儲存weak_ptr,因為是弱引用,所以不會造成ObserverSubject之間的迴圈引用,也就避免了記憶體洩漏。同時weak_ptr::lock()也能很容易的做到被管理物件的執行緒安全。
但是在Observer的建構函式中,我的本意是Obeserver構造之時即註冊到Subject的觀察列表中,實現如下:

class Observer : std
::enable_shared_from_this<Observer>{ public: Observer(std::shared_ptr<Subject>& sbj){ sbj->registerObserver(std::shared_from_this()); //... } //... }

看似想法很美好,甚至都能取一個ORIC(Observer-Registration-Is-Construction)的新名詞了,模仿RAII,手動滑稽。
但是當測試的時候老是給我報錯,最後定位到Observer

的建構函式中,看了很久…幡然醒悟。
Observer的建構函式未執行完畢之前,this指標都是不存在的,或者說是一個殘廢的指標物件,那麼怎麼能呼叫std::shared_from_this()來獲得一個包裹this指標的智慧指標物件呢!!!

可以說是肥腸搞笑又低階的錯誤了。。。。。。。。。
那麼Observer的註冊也只能手動進行了。無礙,只是不那麼優雅罷了。

Anyway,std::enable_shared_from_this & std::shared_from_this是非常好的管理this指標的方法(如果你有需要的話)。只要不在建構函式裡瞎呼叫就行了。