3

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

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

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

首先,我可以使用我已经创建的 cv_scoped 类。我将它重命名为cv_ptr然后使用智能指针,如下所示std::auto_ptr<cv_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。我不想覆盖全局运算符 delete,因为我只希望它适用于 CvMat(和其他一些)类型。但是,我无法更改库,因此无法添加operator delete到 CvMat 结构中。所以我不知道这将如何工作。

第三,我可以重写我自己的auto_ptr,scoped_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
}

在我的情况下你会怎么做?请帮我解决这个问题。

4

3 回答 3

6

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_ptrandscoped_ptr是更轻量级的类,因此没有自定义删除器的功能,但如果您已准备好应对较小的开销,则shared_ptr可以在它们的位置使用。

于 2009-01-31T10:18:41.720 回答
4

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);
}
于 2009-01-31T17:16:29.240 回答
3

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

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”的大杂烩听起来像是头痛和不必要的并发症。

于 2009-01-31T10:14:53.990 回答