目標
在 class 的 member function 裡面取得自己的 shared_ptr 或(weak_ptr)。
Example: 把底下的 code 轉成用智慧指標來管理記憶體的版本
struct Node;
struct Node {
Node() = default;
~Node() = default;
void setLeft(Node* l) {
// assume node has no parent
left = l;
left->parent = this;
}
void setRight(Node* r) {
right = r;
right->parent = this;
}
Node* parent;
Node* left;
Node* right;
};
錯誤嘗試?
很遺憾,std::make_shared<Node>(this)
是完全行不通的,他會製造出一個新的 shared_ptr ,也就是說用同一個物件會被 destruct 一次以上。
https://godbolt.org/z/a56q8ajqc
#include <memory>
#include <iostream>
struct Foo {
Foo() = default;
~Foo() {
std::cout << "~Foo()" << std::endl;
}
auto get() {
return std::shared_ptr<Foo>(this);
}
};
int main() {
auto a = std::make_shared<Foo>();
auto b = a->get();
return 0;
}
Result:
Program returned: 139
double free or corruption (out)
Program terminated with signal: SIGSEGV
~Foo()
std::enable_shared_from_this
https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
用法在文件寫得很清楚了:
讓 class 繼承 std::enable_shared_from_this
使用 shared_from_this 或 weak_from_this (C++17)
實作
A common implementation for enable_shared_from_this is to hold a weak reference (such as std::weak_ptr) to *this. For the purpose of exposition, the weak reference is called weak-this and considered as a mutable std::weak_ptr member.
The constructors of std::shared_ptr detect the presence of an unambiguous and accessible (i.e. public inheritance is mandatory) enable_shared_from_this base and assign the newly created std::shared_ptr to weak-this if not already owned by a live std::shared_ptr.
也就是說,std::shared_ptr 在建構時,會檢查 instance 是不是繼承自 enable_shared_from_this ,是的話就把自己塞到 enable_shared_from_this 內部的 weak_ptr 。
問題:假如 instance 不是 shared_ptr 形式會怎麼樣?
https://godbolt.org/z/jjfsh49MW
#include <memory>
struct Foo : public std::enable_shared_from_this<Foo> {
auto get() {
return shared_from_this();
}
};
int main() {
Foo a;
auto b = a.get();
return 0;
}
結果噴出:
Program returned: 139
terminate called after throwing an instance of 'std::bad_weak_ptr'
what(): bad_weak_ptr
Program terminated with signal: SIGSEGV
從實作上就可以得知,因為 enable_shared_from_this 內部存的 weak_ptr 裡面是空的,然後在 shared_from_this 時嘗試將它轉成 shared_ptr,所以失敗。
所以需要避免使用者直接建立一個 instance 例如:把 constructor 設為 private,並且提供一個 static function 負責 create shared_ptr。
Code?
#include <memory>
#include <iostream>
class Node;
using NodePtr = std::shared_ptr<Node>;
using WeakNodePtr = std::weak_ptr<Node>;
class Node : public std::enable_shared_from_this<Node> {
public:
static NodePtr create() {
return NodePtr(new Node());
};
~Node() {
std::cout << "~Node()" << std::endl;
}
NodePtr getParent() const {
return parent_.lock();
}
void setLeft(NodePtr node) {
// assume node has no parent
left_ = node;
left_->parent_ = weak_from_this();
}
void setRight(NodePtr node) {
right_ = node;
right_->parent_ = weak_from_this();
}
private:
Node() = default;
WeakNodePtr parent_;
NodePtr left_;
NodePtr right_;
};
int main() {
auto root = Node::create();
auto node1 = Node::create();
auto node2 = Node::create();
root->setLeft(node1);
root->setRight(node2);
return 0;
}