Умные указатели с библиотекой, написанной на С - PullRequest
2 голосов
/ 31 января 2009

Я использую C ++ с библиотекой OpenCV, которая является библиотекой обработки изображений, хотя это не относится к этому вопросу. В настоящее время у меня есть дизайнерское решение.

OpenCV, будучи библиотекой C, имеет свои структуры данных (такие как CvMat), объявленные как структуры. Для их создания вы используете такие функции, как cvCreateMat, а для их выпуска вы используете такие функции, как cvReleaseMat. Будучи программистом на C ++, я создал специальный класс cv_scoped, который автоматически вызывал бы cvReleaseMat, когда он выходил из области видимости (например, boost::scoped_ptr).

Теперь я понимаю, что мне хотелось бы использовать auto_ptr и shared_ptr также в случаях. Я просто чувствую, что написание кода для моих собственных классов cv_auto_ptr и cv_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_ptr и shared_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
}

Что бы вы сделали в моей ситуации? Пожалуйста, помогите мне разобраться с этим.

Ответы [ 3 ]

6 голосов
/ 31 января 2009

Один из подходов, который вы могли бы рассмотреть, заключается в том, чтобы использовать тот факт, что 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_ptr и scoped_ptr намного более легкие классы, поэтому не имеют функциональности для пользовательских средств удаления, но если вы готовы к небольшим накладным расходам, тогда вместо них можно использовать shared_ptr.

4 голосов
/ 31 января 2009

auto_ptr действительно разработан для RAII в классе C ++ с конструкциями / деструкторами, которые вы используете здесь, для вещей, которые, вероятно, не должны использоваться (но могут).

В любом случае, хотите ли вы иметь возможность использовать ваш объект 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);
}
3 голосов
/ 31 января 2009

Если все, что вас волнует, это безопасность исключений, делайте это каждый раз, когда вы используете матрицы:

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", звучит как рецепт от головной боли и ненужного осложнения.

...