2008-11-18 20 views
6

Me he encontrado con un problema complicado en algunos códigos C++, que se describe más fácilmente con el código. Tengo clases que son algo así como:Anulando una variable miembro en C++

class MyVarBase 
{ 
} 

class MyVar : public MyVarBase 
{ 
    int Foo(); 
} 

class MyBase 
{ 
public: 
    MyBase(MyVarBase* v) : m_var(v) {} 
    virtual MyVarBase* GetVar() { return m_var; } 
private: 
    MyVarBase* m_var; 
} 

También tengo una subclase de MyBase que tiene que tener un miembro de tipo MiVar porque tiene que llamar Foo. Mover la función Foo a MyVarBase no es una opción. ¿Tiene sentido hacer esto:

class MyClass : public MyBase 
{ 
public: 
    MyClass(MyVar* v) : MyBase(v), m_var(v) {} 
    MyVar* GetVar() { return m_var; } 
private: 
    MyVar* m_var; 
} 

Esto parece funcionar, pero se ve muy mal y no estoy seguro de si se va a producir una pérdida de memoria o romper un constructor de copia. Mis otras opciones podrían ser nombrar la variable MyVar en MyClass algo más, pero que sea igual al puntero m_var en la base, o modelar MyBase en el tipo MyVar.

Todas estas opciones no parecen ideales, así que quería saber si alguien más se ha encontrado en una situación como esta y si hay una buena manera de hacerlo funcionar.

Respuesta

13

La forma correcta de hacerlo es tener la variable solo en la clase base. A medida que la clase derivada sabe que debe ser de tipo dinámico MyVar, esto es totalmente razonable:

class MyClass : public MyBase 
{ 
public: 
    MyClass(MyVar* v) : MyBase(v) {} 
    MyVar* GetVar() { return static_cast<MyVar*>(MyBase::GetVar()); } 
} 

Desde MiVar se deriva de MyVarBase, los diferentes retorno de tipos de GetVar aún funcionaría si getvar era virtual (como es el caso aquí). Tenga en cuenta que con ese método, no debe haber ninguna función en MyBase que pueda restablecer el puntero a algo diferente, obviamente.

Tenga en cuenta que static_cast es el modelo correcto en ese caso. Usando dynamic_cast, según lo propuesto por un comentador, le dirá a los lectores y usuarios de GetVar que MyBase::GetVar() podría devolver un puntero a un objeto que no sea de tipo MyVar. Pero eso no refleja nuestra intención, ya que solo pasas MyVar. Ser consecuente es lo más importante en el desarrollo de software. Lo que podría hacer es afirmar que no es nulo. Abortará en tiempo de ejecución con un mensaje de error en la depuración-construye de su proyecto:

MyVar* GetVar() { 
    assert(dynamic_cast<MyVar*>(MyBase::GetVar()) != 0); 
    return static_cast<MyVar*>(MyBase::GetVar()); 
} 
+0

Usaría dynamic_cast <> en lugar de static_cast <> en este caso. Creo que capta mejor la intención. – coryan

+0

No solo eso, sino que el retorno de dynamic_cast <> devolverá null si v no es un MyVar. – KeithB

+1

no coryan. dynamic_cast sería peor. ya que los lectores pensarían que GetVar podría devolver MyVarBase, lo que sería incorrecto –

2

sin saber más del contexto, es difícil decir con certeza, pero me gustaría reconsiderar si necesita esta jerarquía de clases en primer lugar. ¿Realmente necesitas las clases MyVarBase y MyBase? ¿Podrías salirte con la composición en lugar de la herencia? Tal vez no necesites la jerarquía de clases en absoluto, si configuras las funciones y clases que funcionan con las clases anteriores. Quizás CRTP también puede ayudar. Existen muchas alternativas al uso de funciones virtuales (y hasta cierto punto, herencia en general) en C++.

De todos modos, su propia solución sugerida ciertamente no debería filtrar ninguna memoria (ya que no asigna ninguna), y tampoco rompería el constructor de copia predeterminado (ya que solo realiza una copia de miembro a usuario). solo tiene un miembro extra en la clase derivada, pero eso también se copia). Por supuesto, obtienes el problema adicional de mantener las dos variables sincronizadas, por lo que todavía no me gusta mucho esa solución.

Alternativamente, es posible que pueda eliminar la variable de la clase base. Haga que la función GetVar() sea puramente virtual, por lo que solo se implementa en clases derivadas, que pueden definir la variable miembro de la forma que quiera.

0

Creo que su mención de plantillas puede ser una buena opción, así que algo como:

class MyVarBase 
{ 
}; 

class MyVar : public MyVarBase 
{ 
    int Foo(); 
}; 

template <class T> class MyBase 
{ 
public: 
    MyBase(T* v) : m_var(v) {} 
    T* GetVar() { return m_var; } 

private: 
    T* m_var; 
}; 

class MyClass : public MyBase<MyVar> 
{ 
public: 
    MyClass(MyVar* v) : MyBase(v) {} 
}; 

Sin embargo, esto dependerá de qué clases realidad se puede cambiar. Además, la definición de 'MyClass' podría ser redundante, a menos que tenga otros miembros más allá de la clase MyBase.

Cuestiones relacionadas