【问题标题】:Smart pointers with a library written in C带有用 C 编写的库的智能指针
【发布时间】:2010-10-04 15:46:16
【问题描述】:

我将 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<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_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
}

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

【问题讨论】:

    标签: c++ opencv smart-pointers raii


    【解决方案1】:

    您可以考虑的一种方法是使用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 代替它们。

    【讨论】:

    • 哇,这听起来正是我所需要的,至少对于 shared_ptr 而言。谢谢!
    • 嗯,为什么是函子?为什么不直接将 cvReleaseMat 作为删除器传递呢?
    • 没有真正的原因;我想是习惯。在某些情况下,使用仿函数可能会更有效,因为它的主体可以在使用时内联。在这种情况下,我怀疑它有什么不同。
    • 正确。你能详细说明函子更有效吗?我不明白为什么内联比函数指针更容易
    • 基本上对于函数指针来说,它的类型只封装了它的签名,而仿函数的类型也封装了它的行为。在编译时,如果你是一个被赋予函数指针的函数,你必须调用它来影响它的行为,而仿函数的 operator() 可能是已知的。
    【解决方案2】:

    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);
    }
    

    【讨论】:

    • 我考虑了大约 15 分钟,我认为您可能会在这里找到最佳解决方案。与此解决方案和其他解决方案的区别是微妙但非常重要的。在接受这个答案之前,我会考虑更长的时间。
    【解决方案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”的大杂烩听起来像是让人头疼和不必要的并发症。

    【讨论】:

    • 我明白你的意思,但我想我可能会为此寻求自己的智能指针。我认为因为有这么多不同的功能和类型,我觉得我正在重新发明轮子,重写他们的签名。不过谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 2021-12-26
    • 1970-01-01
    • 2011-03-23
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多