我会开口说我已经研究了几天,试图找出做这件事的“正确方法”。经过大量关于RAII /游泳池设计/智能指针的谷歌搜索,并且没有明确的结论(除了可能没有绝对的“正确方法”),我想也许是时候让更有知识的人指出我正确的方向
我正在构建一个对象池,如果需要,我正在尝试确保客户端代码可以使用RAII。
涉及3个实体:
我在下面提供了一个简单的例子,说明了我的想法。在函数DoSomethingElse()
中,您可以看到我正在追求的内容 - 我获得了对Wrapper的引用,并且在范围的末尾,调用了它的dtor,并且资源被释放回池中。 / p>
我的问题与Factory::GetResource()
的定义有关。这里介绍的简化版本每次只分配一个新版本;我的实际实现检查池是否有可用资源(如果没有可用资源则创建一个),将其标记为正在使用,并返回对它的引用。
我宁愿避免为资源定义一个正确的拷贝ctor,因此通过引用返回,而不是按值返回。资源保证比调用者更长,并且控制器在应用程序的整个生命周期内保持所有权 - 他们没有交给客户端代码进行生命周期管理。当然,如果客户要求直接引用,即没有Wrapper,则所有投注均已关闭。
这个设计听起来好吗?使用shared_ptr会更好吗?还是其他一些机制/设计?
感谢您的时间。
#include <iostream>
#include <vector>
using namespace std;
static int seq = 0; // POOR MAN'S SEQUENCE FOR INSTANCE IDs
class Resource
{
public:
Resource() : id(seq++) { cout << "Resource ctor: " << id << endl; }
~Resource() { cout << "Resource dtor: " << id << endl; }
private:
int id;
};
class Wrapper
{
public:
// ON ACTUAL IMPLEMENTATION, NOTIFY THE CONTROLLER OF THE RELEASE
~Wrapper()
{ cout << "Wrapper dtor: " << id << "Welease Bwian! Ee, I mean, the wesouwce" << endl; }
explicit Wrapper(Resource& r) : id(seq++), res(r)
{ cout << "Wrapper ctor: " << id << endl; }
int getID() const { return id; }
private:
int id;
Resource& res;
};
class Controller
{
public:
~Controller() { for (auto r : allres) delete r; }
Resource& GetResource();
private:
// SIMPLIFIED. I'M USING Boost PTR CONTAINER
vector<Resource *> allres;
};
// SIMPLIFIED. IT WOULD ACTUALLY GET A RESOURCE FROM THE POOL
Resource& Controller::GetResource()
{
Resource* newres = new Resource();
allres.push_back(newres);
return *(newres);
}
// SIMULATE GLOBAL CONTEXT
Controller& GetController()
{
static Controller f;
return f;
}
void DoSomething(Wrapper& wr)
{
cout << "DoSth INI" << endl;
cout << wr.getID() << endl;
cout << "DoSth END" << endl;
}
void DoSomethingElse()
{
cout << "DoSthElse INI" << endl;
Wrapper w(GetController().GetResource());
DoSomething(w);
cout << "DoSthElse END" << endl;
}
int main(int argc, char *argv[])
{
cout << "main INI" << endl;
cout << "Calling DoSthElse" << endl;
DoSomethingElse();
cout << "Called DoSthElse" << endl;
cout << "main END" << endl;
}
答案 0 :(得分:2)
您所描述的情况是资源实际上是由控制器拥有。资源对象的生命周期由Controller管理。
资源的用户有效地“锁定”资源,将其标记为“正在使用”,但他们并不拥有它。它们不影响其寿命。 (你可以说他们拥有一个锁,然后 是他们需要管理的资源)
因此,我建议使用自定义删除器创建类似std::unique_ptr<Resource>
的内容。 (可以通过controller.getResource()
调用
用户可以使用这个unique_ptr
他们喜欢的内容:它不可复制,但可以移动,一旦超出范围,就会调用自定义删除器,这会将其标记为“未使用”控制器,有效地将其返回池中
通过这种方式,您可以按值返回一个对象,这对于客户端来说很简单,并且您可以避免暴露“未解包的”Resource对象:客户端总是将它们包装在{{1}中},这消除了很多潜在的错误。
答案 1 :(得分:1)
请注意,在当前代码中,Controller无法告知调用者何时使用Wrapper / Resource。这意味着当你开始实现Controller :: GetResource时,控制器无法知道它是否可以返回先前创建的资源。
通常这样的设计包括在Wrapper析构函数中调用的Controller :: ReleaseResource。这意味着当构造Wrapper时它获取资源,然后当它被破坏时它释放资源。这正是RAII。