Ponteiros inteligentes com uma biblioteca escrita em C
-
20-08-2019 - |
Pergunta
Eu estou usando C ++ com a biblioteca OpenCV, que é uma imagem de processamento de biblioteca, embora isso não é relevante para esta pergunta. Atualmente tenho uma decisão de projeto para fazer.
OpenCV, sendo uma biblioteca C, tem suas estruturas de dados (tais como CvMat) declarados como estruturas. Para criá-los, você pode usar funções como cvCreateMat, e para libertá-los, você pode usar funções como cvReleaseMat. Ser um programador C ++, eu criei uma classe cv_scoped
especial que chamaria automaticamente cvReleaseMat quando ele saiu do escopo (como boost::scoped_ptr
).
O que eu estou percebendo agora é que eu gostaria de poder usar auto_ptr
e shared_ptr
em casos assim. Eu apenas sinto que escrever código para minhas próprias classes cv_auto_ptr
e cv_shared_ptr
seria uma má ideia, para não mencionar um desperdício de tempo. Então eu fui à procura de soluções, e eu vim acima com três possibilidades.
Primeiro , eu poderia usar a classe cv_scoped eu já fiz. Eu renomeá-lo para cv_ptr
e depois usar ponteiros inteligentes assim: std::auto_ptr<cv_ptr>
. A coisa chata sobre isso, porém, é, eu sempre tenho que dereference duas vezes:
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
Eu sei que parece que eu poderia declarar uma conversão implícita, mas eu não podia realmente - a maioria das funções do OpenCV ter o parâmetro void * - então nenhuma conversão implícita seria chamado. Eu realmente como uma maneira de fazer isso, onde eu não tenho que fazer o duplo dereference.
Segunda , eu poderia de alguma forma substituir operator delete
. Eu não quero substituir o operador global de eliminar, porque eu só quero que isso se aplica a CvMat (e alguns outros) tipos. No entanto, eu não posso mudar a biblioteca, então eu não posso adicionar operator delete
para a estrutura CvMat. Então, eu não sei como isso iria funcionar.
Third , eu poderia simplesmente reescrever minha própria auto_ptr
, scoped_ptr
e shared_ptr
. Eles não são grandes classes de modo que não seria muito difícil, mas eu sinto que isso é mau design. Se eu fosse fazer isso, eu provavelmente faria algo ao longo destas linhas:
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
}
O que você faria na minha situação? Por favor, me ajudar a descobrir isso.
Solução
O auto_ptr são realmente concebido para RAII na classe C ++ com construções / destruidores você está empurrando seus usos aqui para coisas que eles provavelmente não devem ser usados ??para (mas pode).
De qualquer forma don'y você quer ser capaz de usar seu C ++ objeto como se fosse uma variável de pilha normal sem alocar dinamicamente cada vez?
A solução padrão para o problema é criar um invólucro com construtor / destruidor.
Mas para torná-lo utilizável pelas funções C basta adicionar um operador de conversão interna para que ele auto-magicamente converte-se de volta para o objeto C quando passado para uma função C
Escreva uma classe wrapper.
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);
}
Outras dicas
Uma abordagem que você poderia considerar é usado o fato de que std::tr1::shared_ptr
tem a funcionalidade para fornecer um deleter personalizado. Eu não tenho nenhuma familiaridade com OpenCV por isso estou inferindo a partir do que você escreveu.
struct CvMatDeleter
{
void operator( CvMat* p ) { cvReleaseMat( p ) ; }
};
void test()
{
std::tr1::shared_ptr< CvMat > pMat( cvCreateMat(320, 240, CV_32FC3), CvMatDeleter() );
// . . .
}
Porque o deleter é loja no ponteiro compartilhada você pode simplesmente usá-lo como normal e quando o ponteiro bruto compartilhada, finalmente, precisa ser excluído, cvReleaseMat
serão chamados conforme a necessidade. Note-se que auto_ptr
e scoped_ptr
são classes muito mais leves por isso não tem a funcionalidade para Deleters personalizados, mas se você está preparado para a pequena sobrecarga, em seguida, shared_ptr
pode ser usado em seu lugar.
Se tudo o que importa é a segurança de exceção, fazer isso toda vez que você usar matrizes:
void f() {
try {
CvMat* mat = cvCreateMat(320, 240, CV_32FC3));
// ...
} catch(...) {
cvReleaseMat(mat);
throw;
}
cvReleaseMat(mat);
}
Se, por outro lado, você quer um sã solução, ir a milha extra e escrever um invólucro completo.
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;
};
}
Indo o caminho do meio, usando um podge Hodge de ponteiros inteligentes genéricos e "cv_ptrs" soa como uma receita para dores de cabeça e uma complicação desnecessária.