栈对象的c++shared_ptr
c++ shared_ptr of stack object
我最近一直在学习托管指针,遇到了以下场景。
我正在为游戏视图实现一个模型/控制器类。我的视图,将渲染模型中的内容。很直接。在我的主要函数中,我像这样实例化所有三个:
RenderModel m;
m.AddItem(rect); // rect gets added just fine, it's an "entity" derivee
RenderView v;
v.SetModel(m);
我的渲染视图类非常简单:
class RenderView
{
public:
explicit RenderView();
~RenderView();
void Update();
void SetModel(RenderModel& model);
private:
// disable
RenderView(const RenderView& other);
RenderView& operator=(const RenderView& other);
// private members
boost::scoped_ptr<RenderModel> _model;
};
setView的实现非常标准:
void RenderView::SetModel(RenderModel& model)
{
_model.reset(&model);
}
关键是,视图将模型存储在智能指针中。然而,总的来说,模型是在堆栈上分配的。当程序退出时,内存会被删除两次。这是有道理的。我目前的理解告诉我,存储在smart_ptr(任何类型(中的任何东西都不应该在堆栈上分配。
在完成了所有上述设置之后,我的问题很简单:我如何规定没有在堆栈上分配参数?接受智能指针作为参数是唯一的解决方案吗?即使在那时,我也不能确保使用我的视图类的人不会做一些不正确的事情,比如:
// If I implemented SetModel this way:
void RenderView::SetModel(const std::shared_ptr<RenderModel>& model)
{
_model.reset(&*model);
}
RenderModel m;
RenderView v;
std::shared_ptr<RenderModel> ptr(&m); // create a shared_ptr from a stack-object.
v.SetModel(ptr);
如何指定堆栈上没有分配参数?
是,要求呼叫者提供std::shared_ptr<RenderModel>
。如果调用者误解了std::shared_ptr
,那是调用者的问题,而不是你的问题。
如果您希望RenderView
成为特定RenderModel
的唯一所有者,请考虑让该函数使用std::unique_ptr
或std::auto_ptr
;通过这种方式,很明显,调用者在调用函数后不应该保留对象的所有权。
或者,如果RenderModel
的复制成本较低,则制作一份副本并使用该副本:
_model.reset(new RenderModel(model));
您可能应该更清楚地定义类的语义。如果你想让RenderView成为RenderModel的所有者,它应该自己创建它(也许可以在构造函数中获得一些与工厂一起使用的标识符(。
我见过接收对象所有权的类,并且明确定义了这些对象必须在堆上,但在我看来,这很容易出错,就像您现在遇到的错误一样。不能将堆栈对象提供给希望它在堆上的智能指针(因为当它想要清理它时,它会对它使用delete(。
您描述想要做什么的方式是完全错误的。在MVP设计模式中,视图不应直接访问模型,而应向演示者发送命令(通过调用演示者的函数(。
不管怎样,其他人已经回答了你的问题:你的模型对象必须在堆上分配,就像这样:
std::shared_ptr<RenderModel> ptr( new RenderModel );
RenderView v;
v.SetModel(ptr);
否则,sharedptr对象将尝试删除堆栈对象。
您应该回过头来思考设计。第一个代码气味是通过引用获取对象,然后尝试将其作为智能指针进行管理。这是错误的。
您应该从决定谁负责资源开始,并围绕资源进行设计,如果RenderView
负责资源的管理,那么它不应该接受引用,而应该接受(智能(指针。如果它是唯一所有者,则签名应采用std::unique_ptr
(如果编译器+库不支持unique_ptr
,则为std::auto_ptr
(,如果所有权被稀释(尽可能使其成为唯一所有者(,则使用shared_ptr
。
但也有其他场景,RenderView
根本不需要管理资源,在这种情况下,如果在RenderView
的生命周期内无法更改,它可以通过引用获取模型并通过引用存储。在这种情况下,RenderView
不负责资源的管理,它不应该尝试delete
(包括通过智能指针(。
class ModelBase
{
//.....
};
class RenderView
{
//..........
public:
template<typename ModelType>
shared_ptr<ModelType> CreateModel() {
ModelType* tmp=new ModelType();
_model.reset(tmp);
return shared_ptr<ModelType>(_model,tmp);
}
shared_ptr<ModelBase> _model;
//......
};
如果模型类构造函数有参数,则可以将参数添加到方法CreateModel((中,并使用C++11完美的转发技术。
您应该要求用户正确地传递输入。首先将输入类型更改为智能指针(而不是引用变量(。其次,让他们用一个什么都不做的正确传递智能指针(例如,下面的NoDelete(。
一个很难对付的方法是检查内存段。堆栈总是会从内核空间向下扩展(根据下面的代码,我认为32位的0xC0000000,64位的0x7fff2507e800(。所以您可以根据内存位置来猜测它是否是堆栈变量。人们会告诉你它不可移植,但它有点可移植,除非你要在嵌入式系统上部署一些东西。
#include <iostream>
#include <memory>
using namespace std;
class foo
{
public:
foo(shared_ptr<int> in) {
cerr << in.get() << endl;
cerr << var.use_count() << endl;
var = in;
cerr << var.use_count() << endl;
};
shared_ptr<int> var;
};
struct NoDelete {
void operator()(int* p) {
std::cout << "Not Deletingn";
};
};
int main()
{
int myval = 5;
shared_ptr<int> valptr(&myval, NoDelete());
foo staticinst(valptr);
shared_ptr<int> dynptr(new int);
*dynptr = 5;
foo dynamicinst(dynptr);
}
简而言之:定义自定义deleter。在堆栈对象上的智能指针的情况下,可以使用自定义删除程序构造智能指针,在这种情况下是"Null deleter"(或"StackObjectDeleter"(
class StackObjectDeleter {
public:
void operator () (void*) const {}
};
std::shared_ptr<RenderModel> ptr(&m, StackObjectDeleter());
StackObjectDeleter将default_delete替换为deleter对象。default_delete只是调用delete(或delete[](。在StackObjectDeleter的情况下,不会发生任何事情。
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理