当我运行此代码时:
#include <iostream>
#include <memory>
struct Adaptee {
int value = 0;
};
class Adapter {
private:
Adaptee* adaptee;
public:
Adapter (Adaptee* a) : adaptee(a) {}
void increaseValueByOne() {adaptee->value++;}
};
int main() {
Adaptee* a = new Adaptee;
Adapter* adapter = new Adapter(a);
adapter->increaseValueByOne();
delete adapter;
std::cout << "a->value = " << a->value << std::endl;
}
我得到输出:a-&gt; value = 1.但是当我运行时:
struct Adaptee {
int value = 0;
};
class Adapter {
private:
std::shared_ptr<Adaptee> adaptee;
public:
Adapter (Adaptee* a) : adaptee (std::shared_ptr<Adaptee>(a)) {}
void increaseValueByOne() {adaptee->value++;}
};
int main() {
Adaptee* a = new Adaptee;
Adapter* adapter = new Adapter(a);
adapter->increaseValueByOne();
delete adapter;
std::cout << "a->value = " << a->value << std::endl;
}
我得到:a-&gt; value = 7738248
这是为什么?不是适配器&gt;适配器的use_count只是从2变为1,所以什么都不应该出错?当我删除行'删除适配器;'一切都很好,但随后出现了泄漏。
答案 0 :(得分:4)
因为在第二种情况下Adaptee
实例被shared_ptr
的析构函数删除delete adapter
,因为它是拥有该对象的最后一个shared_ptr
。请参阅the doc。
如果你想在删除适配器后让适配器保持活动状态,你应该从头开始将它包装到shared_ptr中:
class Adapter {
private:
std::shared_ptr<Adaptee> adaptee;
public:
Adapter (const std::shared_ptr<Adaptee>& a) : adaptee (a) {}
void increaseValueByOne() {adaptee->value++;}
};
int main() {
std::shared_ptr<Adaptee> a(new Adaptee);
Adapter* adapter = new Adapter(a);
adapter->increaseValueByOne();
delete adapter;
std::cout << "a->value = " << a->value << std::endl;
}
答案 1 :(得分:2)
在第一种情况下,行
delete adapter;
对adaptee
没有任何作用。它仍然是一个有效的指针。
在第二种情况下,同一行调用shared_ptr
的析构函数,该析构函数释放adaptee
指向的内存。因此,adaptee
不是一个有效的指针,如果你取消引用它,你可以期待未定义的行为。
答案 2 :(得分:1)
shared_ptr
的原则是在不再使用尖头对象时管理它的删除。
当您create适配器中的shared_ptr
时,它将获取对象的所有权并将使用计数设置为1.删除适配器时,共享指针也是如此。因此,使用计数(您可以使用use_count()
显示)会自动递减并达到零。因此删除对象并指向无效地址。
如果你没有&#39;希望shared_ptr
在删除adapter
时释放a的内存,你也应该将你的原始指针创建为shared_pointer:
int main() {
auto a = std::make_shared<Adaptee>(); //shared pointer instead of Adaptee* a = new Adaptee;
...
}
在类Adapter中添加一个额外的构造函数:
Adapter(std::shared_ptr<Adaptee> a) : adaptee(a) {}
通过shared_ptr
的这种一致使用,当删除适配器时,共享指针的使用次数将变为1,其余代码将按照您的要求运行,与原始版本一样打印1。
答案 3 :(得分:1)
要扩展其他人所说的内容,当您为原始堆分配指向shared_ptr
的指针时,您将对资源的生命周期进行shared_ptr
控制。 shared_ptr
保留指向资源的shared_ptr
个数的引用计数。当该计数为0
时,资源将被清理。
adaptee (std::shared_ptr<Adaptee>(a))
此处,引用计数为1
,因为1
shared_ptr
指向资源a
。
delete adapter;
导致shared_ptr
中的adapter
超出范围。调用shared_ptr
的析构函数,它会减少引用计数。由于引用计数现在为0
,因此delete
会调用a
。
std::cout << "a->value = " << a->value << std::endl;
这里,a不再是有效指针,因为在delete
的析构函数中调用了shared_ptr
。
如果您希望a
保持有效,则应执行以下操作:
struct Adaptee {
int value = 0;
};
class Adapter {
private:
std::shared_ptr<Adaptee> adaptee;
public:
Adapter (std::shared_ptr<Adaptee> a) : adaptee(a) {}
void increaseValueByOne() {adaptee->value++;}
};
int main() {
std::shared_prt<Adaptee> a = std::make_shared<Adaptee>(); //reference count is 1
Adapter* adapter = new Adapter(a); //reference count is 2
adapter->increaseValueByOne();
delete adapter; //reference count is 1
std::cout << "a->value = " << a->value << std::endl;
} // reference count is 0; memory is deallocated