示例程序:
#include <memory>
#include <iostream>
class D;
class C {
public:
C();
void callD();
void replaceD(D* d);
private:
std::shared_ptr<D> d;
};
class D {
public:
D(C* c);
void call();
private:
virtual void print();
C* c;
};
C::C() : d(new D(this)) {}
void C::callD() {
d->call();
}
void C::replaceD(D* d) {
this->d = std::shared_ptr<D>(d);
}
D::D(C* c) : c(c) {}
void D::call() {
c->replaceD(new D(c));
print();
}
void D::print() {
std::cout << "Hello, World!" << std::endl;
}
int main(void) {
auto c = new C();
c->callD();
return 0;
}
(使用gcc:g++ -std=c++11 tmp.cpp -o tmp && ./tmp
)
会发生什么:
C::callD()
来电d->call()
D::call()
来电c->replaceD()
C::replaceD()
重新分配C
的{{1}}指针,导致旧的d
被删除d
尝试调用虚拟方法D::call()
- 但print()
删除了当前的d
实例!解决方法:在C::replaceD()
中插入auto d_ = d;
,以便在C::callD()
完成之前不会删除shared_ptr
。但这看起来很像一个可以优化的未使用变量(尽管g ++似乎并没有在C::callD()
上删除它)。
在-O3
返回之前,任何ptr->method()
来电都不应该提高引用次数,以避免在method()
的{{1}}过早删除时出现此问题?
答案 0 :(得分:1)
您可以将std::shared_ptr
引用传递给C::replaceD
,在C
被删除之前可以获得D
当前void C::replaceD(D* d, std::shared_ptr<D>& tmp)
{
tmp.swap(this->d);
this->d = std::shared_ptr<D>(d);
}
的所有权。
D
然后D::call
可以保持活着直到void D::call()
{
std::shared_ptr<D> tmp {};
c->replaceD(new D(c), tmp);
print();
}
{{1}}
我不得不说,你的代码看起来设计得很差,我首先会考虑是否有更好的设计可以完全避免这个问题。
答案 1 :(得分:1)
在method()返回之前,不应该在任何ptr-&gt; method()调用上增加引用计数,以避免在方法过早删除的情况下出现此问题?
没有。 std::shared_ptr
的语义是严格的指针引用,而不是函数调用。该对象需要在另一个对象或堆栈中引用。你正在删除参考文献。
void C::replaceD(D* d) {
this->d = std::shared_ptr<D>(d);
}
此代码也可能是delete this
而不是c->replaceD(new D(c))
。
void D::call() {
c->replaceD(new D(c));
print();
}
我不确定你要做什么,但是如果你需要保持对D
的引用,你可以重写C::replaceD
。
std::shared_ptr<D> C::replaceD(D* d) {
std::shared_ptr<D> old = this->d;
this->d = std::shared_ptr<D>(d);
return old;
}
然后重写D::call
。
void D::call() {
std::shared_ptr<D> prev = c->replaceD(new D(c));
prev->print();
}