很长一段时间后,我又回到了 C++,继承了一个代码库,其中一部分具有以下代码中的结构。我有两个对象,Wrapped
和,Wrapper
并且正如所料,Wrapper
包装了Wrapped
。有两种工厂方法make_Wrapper
和,make_Wrapped
它们返回指向相应对象的指针。
在下面的代码中,我发现Case1
使用shared_ptr
不起作用,而Case2
使用普通 C++ 指针却起作用。“不起作用”是指Case1 中nz_=10
的值wrapped
被垃圾替换,而 Case2 中的值等于 10。我可以通过 打印 中的值来检查这wrapper.nz_
一点。main
outside_wrapper->print()
我认为这是因为该函数退出后,shared_ptr 指向的对象会被删除make_Wrapper
。我说得对吗?
shared_ptr
通过从make_Wrapper
到 构造函数传递对象的正确方法是什么Wrapper
?
#include <iostream>
#include <memory>
#include <functional>
// this is struct which is to be wrapped
struct Wrapped {
int nz_;
Wrapped(int nz):nz_(nz) {}
inline virtual void destroy() { delete this; };
};
// this is the wrapper
struct Wrapper {
Wrapper(Wrapped& wrapped):wrapped_(wrapped){}
Wrapped& wrapped_;
inline virtual void destroy() { delete this; };
void print() {
std::cout << "from wrapper: wrapped_.nz_=" << wrapped_.nz_<<std::endl;
}
};
// factory function which returns pointer to wrapped object
Wrapped* make_Wrapped(int nz) {
return new Wrapped(nz);
}
// factory function which returns pointer to wrapper object
Wrapper* make_Wrapper(int nz) {
// Case 1
// following line does not work - I think the object pointed to by the shared_ptr is deleted after this function exits
std::shared_ptr<Wrapped> wrapped(make_Wrapped(nz), std::mem_fun(&Wrapped::destroy));
// Case 2
// following line works
// Wrapped *wrapped = new Wrapped(nz);
std::cout<<" from make_Wrapper "<<(*wrapped).nz_<<std::endl;
return new Wrapper(*wrapped);
}
int main() {
int nz=10;
char tempbuffer[80];
std::shared_ptr<Wrapper> outside_wrapper(make_Wrapper(nz), std::mem_fun(&Wrapper::destroy));
outside_wrapper->print();
std::cout << "Enter something to continue ..." << std::endl;
std::cin >> tempbuffer;
return 0;
}
13
最佳答案
2
您可以使用智能指针将Wrapped
对象保存在 中Wrapper
。使用智能指针时,无需原始指针和手动new
/ delete
。也不需要(至少默认情况下)显式destroy()
方法。
在下面的例子中我使用了,它不支持复制,因此在需要时用 d 代替。
std::unique_ptr
在足够时应该是默认的智能指针。
如果您想要共享所有权,则可以使用和std::make_shared
(然后您只需删除std::move
s 即可)。
#include <iostream>
#include <memory>
// this is struct which is to be wrapped
struct Wrapped {
int nz_;
Wrapped(int nz) :nz_(nz) {}
};
// this is the wrapper
struct Wrapper {
Wrapper(std::unique_ptr<Wrapped> wrapped) :wrapped_(std::move(wrapped)) {}
std::unique_ptr<Wrapped> wrapped_;
void print() {
std::cout << "from wrapper: wrapped_.nz_=" << wrapped_->nz_ << std::endl;
}
};
// factory function which returns pointer to wrapped object
std::unique_ptr<Wrapped> make_Wrapped(int nz) {
return std::make_unique<Wrapped>(nz);
}
// factory function which returns pointer to wrapper object
std::unique_ptr<Wrapper> make_Wrapper(int nz) {
auto wrapped = make_Wrapped(nz);
std::cout << " from make_Wrapper " << wrapped->nz_ << std::endl;
auto wrapper = std::make_unique<Wrapper>(std::move(wrapped));
return wrapper;
}
int main() {
int nz = 10;
auto outside_wrapper = make_Wrapper(nz);
outside_wrapper->print();
}
输出:
from make_Wrapper 10
from wrapper: wrapped_.nz_=10
既然您明确询问了std::shared_ptr
,
这里也有一个演示:。
|
一旦一个对象被 包裹,shared_ptr
它将在最后一个指向它的对象被销毁时也被销毁shared_ptr
,这发生在 的末尾make_Wrapper
。
解决方案:
让 Wrapper 存储一个shared_ptr
而不是仅仅一个引用,现在Wrapped
只要还Wrapper
活着就会保持活跃。
struct Wrapper {
Wrapper(std::shared_ptr<Wrapped> wrapped) :wrapped_(std::move(wrapped)) {}
std::shared_ptr<Wrapped> wrapped_;
inline virtual void destroy() { delete this; };
void print() {
std::cout << "from wrapper: wrapped_.nz_=" << wrapped_->nz_ << std::endl;
}
};
// factory function which returns pointer to wrapper object
Wrapper* make_Wrapper(int nz) {
std::shared_ptr<Wrapped> wrapped(make_Wrapped(nz), std::mem_fun(&Wrapped::destroy));
std::cout << " from make_Wrapper " << (*wrapped).nz_ << std::endl;
return new Wrapper(std::move(wrapped));
}
|
–
nz_=10
被 junk 替换,而在 Case2 中,该值等于 10–
–
unique_ptr
@wohlstad 在其答案中展示的方式。第一个选择应该是unique_ptr
(当然,除了需要共享所有权的情况)如该答案中所示。–
destroy
方法执行的是 a,delete this;
则是一种 C++ 反模式,它源自从另一种语言移植 C++ 代码的幼稚做法,而另一种语言使用惯用的 alloc-init-destroy-free 模式。 init 应该在构造函数中,destroy 应该在析构函数中。 不delete this;
。 (还有另一种 C++ 反模式,即先进行两阶段构造,然后再进行 init。)–
|