我想更好地理解shared_ptr 这是我的代码
struct A {
int val_ = 0;
A(int val) : val_(val) { std::cout << "A(" << val_ << ")" << std::endl ; }
~A() { std::cout << "~A(" << val_ << ")" << std::endl ; }
};
int main() {
{
A* a_raw = new A(2);
shared_ptr<A> a2(a_raw);
{
shared_ptr<A> a3(new A(3));
std::cout << "log 2" << std::endl;
a2 = a3;
std::cout << "log 3" << std::endl;
}
std::cout << "log 4" << std::endl;
if (a_raw == nullptr) {
cout << "A_raw is nullptr" << std::endl;
} else {
cout << "A_raw ---------" << a_raw->val_ << std::endl;
}
}
电流输出
A(2)
A(3)
log 2
~A(2)
log 3
log 4
A_raw ---------0
~A(3)
Destructor ~A(2) 被调用是因为行
a2 = a3;
你能详细说明operator=
的逻辑吗?
为什么我看到线
A_raw ---------0
??
我期待A_raw is nullptr
我是否正确理解了复制作业
class my_shared_ptr {
...
my_shared_ptr& operator=(const my_shared_ptr& other) {
if (counter_ and *counter_ > 1) {
*counter_ -= 1;
} else {
delete ptr_;
}
ptr_ = other.ptr_;
counter_ = other.counter_;
if (other.ptr_ != nullptr) {
*counter_ += 1;
}
}
private:
T* ptr_ = nullptr;
size_t* counter_ = nullptr;
a_raw
不是nullptr
的问题与作业无关a3=a2
:
A* a_raw = new A(2);
将在空闲存储上分配一个新的 A 并且堆栈上将有一些内存保存该对象的地址。
shared_ptr<A> a2(a_raw);
将按值获取该地址。这样它就是地址的副本,a2
无法修改原始a_raw
.
因此永远不可能将
a_raw
设置为nullptr
。
作业
a2 = a3
是做什么的?
嗯,operator = 说:
用 r 管理的对象替换被管理的对象。
如果 *this 已经拥有一个对象,并且它是最后一个拥有它的 shared_ptr,并且 r 与 *this 不同,则通过拥有的删除器销毁该对象。
因此,在接管新所有权之前,
a2
将减少资源的引用计数。在您的例子中,引用计数下降到 0,因此对象 A(2) 被销毁。
然后它获得
a3
的所有权,这将引用计数从 1 增加到 2,因为 a2
和 a3
现在管理相同的资源。您已成功共享一个指针 - 这正是 shared_ptr
所做的。
如前所述,共享指针无法将您的原始
a_raw
设置为nullptr
,因为它们没有对它的引用。
智能指针的想法是你根本不用处理原始指针。你应该能够完全摆脱
A*
。所以最好一开始就不要有原始指针:
int main() {
{
shared_ptr<A> a2(new A(2));
{
shared_ptr<A> a3(new A(3));
std::cout << "log 2" << std::endl;
a2 = a3;
std::cout << "log 3" << std::endl;
}
std::cout << "log 4" << std::endl;
}
甚至使用
make_shared
这样您就永远不会尝试为您的 delete
语句搜索匹配的 new
语句。
int main() {
{
shared_ptr<A> a2 = make_unique<A>(2);
{
shared_ptr<A> a3= make_unique<A>(3);
std::cout << "log 2" << std::endl;
a2 = a3;
std::cout << "log 3" << std::endl;
}
std::cout << "log 4" << std::endl;
}
关于复制分配的更新问题:
从给定的代码中,我看不到
counter_
在哪里初始化。
该方法缺少
return
语句,它返回的内容非常重要。
*counter_ -= 1;
- 如果在这个操作之后计数器== 0
,指针应该是delete
d.
在
nullptr
中检查if (other.ptr_ != nullptr)
是没有必要的,恕我直言。由于您可以 delete
nullptr
,您只需像其他任何指针一样跟踪它的引用。