带有用 C 编写的库的智能指针

发布于 2024-07-13 12:15:57 字数 1709 浏览 6 评论 0原文

我将 C++ 与 OpenCV 库一起使用,这是一个图像处理库,尽管与这个问题无关。 目前我需要做出一个设计决定。

OpenCV 作为一个 C 库,其数据结构(例如 CvMat)被声明为结构体。 要创建它们,您可以使用 cvCreateMat 等函数,而要释放它们,您可以使用 cvReleaseMat 等函数。 作为一名 C++ 程序员,我创建了一个特殊的 cv_scoped 类,当它超出范围时,它会自动调用 cvReleaseMat(如 boost::scoped_ptr)。

我现在意识到,我希望在某些情况下也可以使用 auto_ptrshared_ptr 。 我只是觉得为自己的 cv_auto_ptrcv_shared_ptr 类编写代码是一个坏主意,更不用说浪费时间了。 所以我一直在寻找解决方案,并提出了三种可能性。

首先,我可以使用我已经创建的 cv_scoped 类。 我将其重命名为 cv_ptr,然后使用智能指针,如下所示:std::auto_ptr。 但令人烦恼的是,我总是必须取消引用两次:

std::auto_ptr<cv_ptr> matrix(cv_ptr(cvCreateMat(320, 240, CV_32FC3)));
cvPow(matrix.get()->get()); // one get for the auto_ptr, one for the cv_ptr

我知道看起来我可以声明隐式转换,但实际上我不能 - 大多数 OpenCV 函数都有参数 void* - 所以没有隐式转换会被称为。 我真的很想要一种不需要进行双重解引用的方法。

第二,我可以以某种方式覆盖operator delete。 我不想覆盖全局运算符删除,因为我只想将其应用于 CvMat (和其他一些)类型。 但是,我无法更改库,因此无法将 operator delete 添加到 CvMat 结构中。 所以我不知道这将如何运作。

第三,我可以重写自己的 auto_ptrscoped_ptrshared_ptr。 它们的班级规模不大,所以不会太困难,但我只是觉得这是一个糟糕的设计。 如果我这样做,我可能会做这样的事情:

class cv_auto_ptr {
public:
  cv_auto_ptr();
  ~cv_auto_ptr();

  // each method would just be a proxy for the smart pointer
  CvMat* get() { return this->matrix_.get()->get(); }
  // all the other operators/methods in auto_ptr would be the same, you get the idea

private:
  auto_ptr<cv_ptr> matrix_; // cv_ptr deletes CvMat properly
}

如果我遇到这种情况,你会怎么做? 请帮我解决这个问题。

I'm using C++ with the OpenCV library, which is a library image-processing although that's not relevant for this question. Currently I have a design decision to make.

OpenCV, being a C library, has its data structures (such as CvMat) declared as structs. To create them, you use functions like cvCreateMat, and to release them, you use functions like cvReleaseMat. Being a C++ programmer, I created a special cv_scoped class which would automatically call cvReleaseMat when it went out of scope (like boost::scoped_ptr).

What I'm realising now is that I wish I could use auto_ptr and shared_ptr in cases as well. I just feel that writing code for my own cv_auto_ptr and cv_shared_ptr classes would be a bad idea, not to mention a waste of time. So I've been looking for solutions, and I've come up with three possibilities.

First, I could use the cv_scoped class I've already made. I'd rename it to cv_ptr and then use smart pointers like so: std::auto_ptr<cv_ptr>. The annoying thing about this though is, I'd always have to dereference twice:

std::auto_ptr<cv_ptr> matrix(cv_ptr(cvCreateMat(320, 240, CV_32FC3)));
cvPow(matrix.get()->get()); // one get for the auto_ptr, one for the cv_ptr

I know it looks like I could declare an implicit conversion, but I couldn't actually - most of OpenCV's functions have the parameter void* - so no implicit conversion would be called. I would really like a way of doing this where I didn't have to do the double dereference.

Second, I could somehow override operator delete. I don't want to override the global operator delete because I'd only want this to apply to CvMat (and a few other) types. However, I can't change the library, so I can't add operator delete to the CvMat struct. So I don't know how this would work.

Third, I could just rewrite my own auto_ptr, scoped_ptr, and shared_ptr. They're not large classes so it wouldn't be too difficult, but I just feel like this is bad design. If I were to do this, I would probably do something along these lines:

class cv_auto_ptr {
public:
  cv_auto_ptr();
  ~cv_auto_ptr();

  // each method would just be a proxy for the smart pointer
  CvMat* get() { return this->matrix_.get()->get(); }
  // all the other operators/methods in auto_ptr would be the same, you get the idea

private:
  auto_ptr<cv_ptr> matrix_; // cv_ptr deletes CvMat properly
}

What would you do in my situation? Please help me figure this one out.

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

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

发布评论

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

评论(3

等往事风中吹 2024-07-20 12:15:57

您可以考虑的一种方法是利用 std::tr1::shared_ptr 具有提供自定义删除器的功能。 我对 OpenCV 不熟悉,所以我从你所写的内容中推断。

struct CvMatDeleter
{
    void operator( CvMat* p ) { cvReleaseMat( p ) ; }
};

void test()
{
    std::tr1::shared_ptr< CvMat > pMat( cvCreateMat(320, 240, CV_32FC3), CvMatDeleter() );
    // . . .
}

因为删除器存储在共享指针中,所以您可以照常使用它,当最终需要删除共享原始指针时,将根据需要调用cvReleaseMat。 请注意,auto_ptrscoped_ptr 是更轻量级的类,因此不具备自定义删除器的功能,但如果您准备好承受较小的开销,那么 shared_ptr< /code> 可以用来代替它们。

One approach that you could consider is to used the fact that std::tr1::shared_ptr has the functionality to provide a custom deleter. I have no familiarity with OpenCV so I'm inferring from what you've written.

struct CvMatDeleter
{
    void operator( CvMat* p ) { cvReleaseMat( p ) ; }
};

void test()
{
    std::tr1::shared_ptr< CvMat > pMat( cvCreateMat(320, 240, CV_32FC3), CvMatDeleter() );
    // . . .
}

Because the deleter is store in the shared pointer you can just use it as normal and when the shared raw pointer finally needs to be deleted, cvReleaseMat will be called as required. Note that auto_ptr and scoped_ptr are much lighter classes so don't have the functionality for custom deleters, but if you're prepared for the small overhead then shared_ptr can be used in their place.

左秋 2024-07-20 12:15:57

auto_ptr 实际上是为 C++ 类上的 RAII 设计的,具有构造/析构函数,您在这里将它们的用途推向它们可能不应该用于(但可以)的用途。

无论如何,您是否希望能够像普通堆栈变量一样使用 C++ 对象,而无需每次都动态分配?

问题的标准解决方案是创建一个带有构造函数/析构函数的包装器。
但要使其可供 C 函数使用,只需添加一个内部强制转换运算符,以便在传递给 C 函数时它会自动将自身神奇地转换回 C 对象

编写一个包装类。

class Mat
{
    CvMat* impl;
    public:
        Mat(/* Constructor  Arguments */)
        {
            impl = cvCreateMat(/* BLAH */);
        }
        ~Mat()
        {
            cvReleaseMat(impl);
        }
        operator CvMat*()
        {   // Cast opertator. Convert your C++ wrapper object into C object
            // when you use it with all those C functions that come with the
            // library.

            return impl;
        }
};

void Plop(CvMat* x)
{   // Some C function dealing with CvMat
}

int main()
{                            // Don't need to dynamically allocate
    Mat                  m;  // Just create on the stack.
    Plop(m);                 // Call Plop directly

    std::auto_ptr<Mat>   mP(new Mat);
    Plop(*mP);
}

The auto_ptr are really designed for RAII on C++ class with constructs/destructors you are pushing their uses here to things they probably should not be used for (but can).

Anyway don'y you want to be able to use your C++ object as if it was a normal stack variable without dynamically allocating each time?

The standard solution to your problem is to create a wrapper with constructor/destructor.
But to make it usable by the C functions just add an internal cast operator so it auto-magically converts itself back to the C object when passed to a C function

Write a wrapper class.

class Mat
{
    CvMat* impl;
    public:
        Mat(/* Constructor  Arguments */)
        {
            impl = cvCreateMat(/* BLAH */);
        }
        ~Mat()
        {
            cvReleaseMat(impl);
        }
        operator CvMat*()
        {   // Cast opertator. Convert your C++ wrapper object into C object
            // when you use it with all those C functions that come with the
            // library.

            return impl;
        }
};

void Plop(CvMat* x)
{   // Some C function dealing with CvMat
}

int main()
{                            // Don't need to dynamically allocate
    Mat                  m;  // Just create on the stack.
    Plop(m);                 // Call Plop directly

    std::auto_ptr<Mat>   mP(new Mat);
    Plop(*mP);
}
假扮的天使 2024-07-20 12:15:57

如果您关心的只是异常安全,请在每次使用矩阵时执行此操作:

void f() {
    try {
        CvMat* mat = cvCreateMat(320, 240, CV_32FC3));
        // ...
    } catch(...) {
        cvReleaseMat(mat);
        throw;
    }
    cvReleaseMat(mat);
}

另一方面,如果您想要一个合理解决方案,请加倍努力并编写完整的包装器。

namespace cv {

class Mat {
public:
    enum Type { /* ... */ };
    Mat(int w, int h, Type type) {
        impl = cvCreateMat(w, h, intFromType(type));
    }

    ~Mat() {
        cvReleaseMat(impl);
    }

    void pow() { // wrap all operations
        cvPow(impl);
    }

private:
    CvMat* impl;
};

}

走中间路线,使用通用智能指针和“cv_ptrs”的大杂烩,听起来像是令人头痛和不必要的并发症的良方。

If all you care about is exception safety, do this everytime you use matrixes:

void f() {
    try {
        CvMat* mat = cvCreateMat(320, 240, CV_32FC3));
        // ...
    } catch(...) {
        cvReleaseMat(mat);
        throw;
    }
    cvReleaseMat(mat);
}

If, on the other hand, you want a sane solution, go the extra mile and write a full wrapper.

namespace cv {

class Mat {
public:
    enum Type { /* ... */ };
    Mat(int w, int h, Type type) {
        impl = cvCreateMat(w, h, intFromType(type));
    }

    ~Mat() {
        cvReleaseMat(impl);
    }

    void pow() { // wrap all operations
        cvPow(impl);
    }

private:
    CvMat* impl;
};

}

Going the middle way, using a hodge podge of generic smart pointers and "cv_ptrs" sounds like a recipe for headaches and an unnecessary complication.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文