有条件地使用 RAII 的最佳方法

发布于 2024-11-16 08:58:54 字数 656 浏览 2 评论 0原文

我有一个很好的资源管理课程。具体来说,让它成为一个用于管理 FILE* 的 File 类(处理打开和关闭操作)

当资源不需要由我管理并且由其他人负责的情况下,通常的方法是什么?

出于说明目的,我目前有这样的东西:

int main(int argc, char** argv)
{
    File my_file(argv[1]); //I unconditionaly obtain the resource
    //...
    return 0;  //and unconditionally relinquish with the destructor
}

并且想要类似的东西

int main()
{
    if(argc <= 1){
        //use stdin that is already available
    }else{
        //obtain a file from argv[1]
    }
    //...
    if(argc <= 1){
        //nothing to do
    }else{
        //close the file we obtained
    }
}

(但不那么丑陋,更强大,等等......)

I have a nice resource managing class. For concreteness, lets have it be a File class for managing a FILE* (handling the open and close operations)

What is the usual approach when there are cases where the resource doesn't need to be managed by me, and is someone else's responsibility?

For ilustrative purposes, I currently have something like this:

int main(int argc, char** argv)
{
    File my_file(argv[1]); //I unconditionaly obtain the resource
    //...
    return 0;  //and unconditionally relinquish with the destructor
}

And want something like

int main()
{
    if(argc <= 1){
        //use stdin that is already available
    }else{
        //obtain a file from argv[1]
    }
    //...
    if(argc <= 1){
        //nothing to do
    }else{
        //close the file we obtained
    }
}

(but less ugly, more robust, etc...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

欢烬 2024-11-23 08:58:54

boost::shared_ptr 允许您传入自定义析构函数。如果您要包装外部管理的指针,则可以传递无操作:

namespace {
template<typename T>
void noop_destruct(T *) throw() { }
}

template<typename T>
boost::shared_ptr<T> make_dummy_shared_ptr(T *p) {
    return boost::shared_ptr<T>(p, noop_destruct);
}

现在,当您需要真正的 RAII 对象时,请使用普通的 boost::shared_ptr,当您需要假的时,使用这样的适配器 - 它的作用与普通指针完全相同。

boost::shared_ptr allows you to pass in a custom destructor. If you're wrapping an externally-managed pointer, you can pass a no-op:

namespace {
template<typename T>
void noop_destruct(T *) throw() { }
}

template<typename T>
boost::shared_ptr<T> make_dummy_shared_ptr(T *p) {
    return boost::shared_ptr<T>(p, noop_destruct);
}

Now when you need a true RAII object, use a normal boost::shared_ptr, and when you need a fake one, use an adapter like this - it'll act exactly like a normal pointer.

扛起拖把扫天下 2024-11-23 08:58:54

您的 RAII 类已经保留了足够的状态来知道何时销毁它所控制的资源。它还可以包含一个标志,告诉它资源是否应该被销毁,或者您可以在计数器上使用特殊值来指示资源是在类外部控制的。

那么你所需要的就是在获取资源时控制状态的方法。例如,您可以有两个不同的构造函数,或者构造函数上的参数具有默认值。您可以使用 Attach 方法来附加现有资源。这完全取决于你。

Your RAII class is already keeping enough state to know when to destroy the resource it is controlling. It can also contain a flag that tells it if the resource should be destroyed, or you can use a special value on the counter to indicate that the resource is controlled outside the class.

Then all you need is a way to control the state when you obtain the resource. You can have two different constructors for example, or a parameter on the constructor with a default value. You could have an Attach method that attaches an existing resource. It's entirely up to you.

2024-11-23 08:58:54

您可以将是否使用资源的逻辑推送到资源管理类中。那么就不再是有条件的了。只要做

int main(int argc, char** argv)
{
    File my_file(argc > 1 ? argv[1]: NULL); //If NULL, File will point to stdin
    //...
    return 0;  //File's destructor will run, relinquishing resources if necessary.
}

You can push the logic of whether or not to use the resource inside your resource managing class. Then it isn't conditional anymore. Just do

int main(int argc, char** argv)
{
    File my_file(argc > 1 ? argv[1]: NULL); //If NULL, File will point to stdin
    //...
    return 0;  //File's destructor will run, relinquishing resources if necessary.
}
唠甜嗑 2024-11-23 08:58:54

大多数常见模式不允许这样做。但是,您可以允许自定义分配器插件(标准为其容器提供了这些插件),这将允许这些语义。这是一个简短的示例 -

class Allocator {
    File* Allocate(...) {
        return fopen(...);
    }
};
class MyStdinAllocator {
    File* Allocate(...) {
        return ...;
    }
};
template<typename MyAllocator = Allocator> class File {
    File* ptr;
    Allocator alloc;
    File(..., const Allocator& allocref)
    : alloc(allocref) {
        ptr = alloc.Allocate(...);
    }
};

Most common patterns do not allow for this. However, you can allow custom allocator plugins, which the Standard has for it's containers, that will allow for these semantics. This is a brief sample-

class Allocator {
    File* Allocate(...) {
        return fopen(...);
    }
};
class MyStdinAllocator {
    File* Allocate(...) {
        return ...;
    }
};
template<typename MyAllocator = Allocator> class File {
    File* ptr;
    Allocator alloc;
    File(..., const Allocator& allocref)
    : alloc(allocref) {
        ptr = alloc.Allocate(...);
    }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文