微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++句柄类抽象

如何解决C++句柄类抽象

我正在尝试使用类来抽象原始句柄的显式创建和销毁。实际句柄存储为私有类成员(以便用户不会与较低级别的细节交互),它在构造时创建并在销毁时销毁。是否有某种设计模式可以帮助实现以下代码所要实现的目标?

请注意,可能存在大量相互依赖的类,因此用大量友元语句污染每个类既乏味又糟糕。

#include <memory>

// Handle types may vary
typedef uint32_t A_Handle;
typedef uint32_t B_Handle;
typedef int64_t C_Handle;

extern void createA(A_Handle*);
extern void destroyA(A_Handle);
extern void createB(B_Handle*);
extern void destroyB(B_Handle);
extern void createC(C_Handle*,A_Handle,B_Handle);
extern void destroyC(C_Handle,B_Handle);

class A
{
private:
    A_Handle handle_;
public:
    A()
    {
        createA(&handle_);
    }

    ~A()
    {
        destroyA(handle_);
    }

    A(const A&) = delete;

    A& operator=(const A&) = delete;
};

class B
{
private:
    B_Handle handle_;
public:
    B()
    {
        createB(&handle_);
    }

    ~B()
    {
        destroyB(handle_);
    }

    B(const B&) = delete;

    B& operator=(const B&) = delete;
};

class C
{
private:
    C_Handle handle_;
public:
    std::shared_ptr<A> a;
    std::shared_ptr<B> b;

    C(const std::shared_ptr<A>& a,const std::shared_ptr<B>& b)
        : a(a),b(b)
    {
        // Error a->handle_ and b->handle_ is private
        createC(&handle_,a->handle_,b->handle_);
    }

    ~C()
    {
        // Error a->handle_ and b->handle_ is private
        destroyC(handle_,b->handle_);
    }

    C(const C&) = delete;

    C& operator=(const C&) = delete;
};

// ...

int main()
{
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    std::shared_ptr<C> c = std::make_shared<C>(a,b);

    // ...

    return EXIT_SUCCESS;
}

解决方法

是否有某种设计模式可以帮助实现以下代码所要实现的目标?

是的。它被称为资源获取即初始化,或简称RAII。您的第一次尝试是在正确的方向上,但它可能是不完整的。可能需要担心的事情是通常多次“销毁”原始句柄是错误的。因此,您应该建立一个“类不变量”,作为每个成员函数的后置条件,该类的两个实例都不会拥有相同的原始句柄。您的类目前违反了这种不变量。考虑复制实例时会发生什么。有一条经验法则称为五法则(以前称为三法则),有助于建立该不变量。

对于私人访问和避免朋友,一个好的解决方案是提供一个公共getter:

class A
{
public
    A_Handle get_handle() { return handle; }

成员仍然被封装,类的用户将无法打破不变量,因为他们不能修改它。

,

您不必为此推出自己的解决方案。相反,您可以将 std::unique_ptr 与自定义删除器一起使用,该删除器知道如何在 unique_ptr 超出范围时销毁句柄。

这是一个使用 FILE * 作为“句柄”的示例:

#include <cstdio>
#include <memory>

int main ()
{
    FILE *f = std::fopen ("myfile","r");
    if (f)
    {
        std::unique_ptr <FILE,decltype (&std::fclose)> upf (f,std::fclose);
        // do things with the open file
        // ...
        // file will be closed here,when upf goes out of scope
    }
}

如果您的句柄不是指针类型,您可以将其强制转换为 void *(大多数句柄适合 void *)。例如:

#include <sys/stat.h>
#include <fcntl.h>
#include <cstdint>
#include <memory>

int main ()
{
    int fd = open ("myfile",O_RDONLY);
    if (fd >= 0)
    {
        std::unique_ptr <void,void (*) (void *)> upfd
            ((void *) (uintptr_t) fd,[] (void *fd) { close ((int) (uintptr_t) fd); });
        // do things with the open file
        // ...
        // file will be closed here,when upfd goes out of scope
    }
}

当然,您可以为那些看起来很复杂的模板定义类型别名以使代码更简洁。

std::unique_ptr 有一些不错的特性,包括删除的复制构造函数和可行的移动构造函数。此外,如果您需要共享所有权语义(又名引用计数),您可以使用 std::shared_ptr 使用类似的技巧。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。