2011-05-05 13 views
13

Por lo que yo sé, no es posible llamar al constructor de la clase base. La única forma que conozco es la siguiente:Llamar al constructor de la clase base después de algunas otras instrucciones en C++

MyClass::MyClass(/* args */) : Base(/* args */) 
{ 
    // ... 
} 

pero esto invocaría al constructor al principio. ¿Hay alguna manera de llamarlo a otro lugar en el constructor? Algo como esto:

MyClass::MyClass(/* args */) 
{ 
    // ... instructions 
    Base::Base(/* args */); 
    // ... other_instructions 
} 

De acuerdo con esta What are the rules for calling the superclass constructor? pregunta que entiendo que no hay manera pero he leído here y supuso que era posible, pero si lo intento me sale:

error: invalid use of 'class Base'. 

Estoy haciendo ¿algo mal? ¿Es posible hacer esto de alguna manera o hay alguna otra solución posible a esta necesidad?

Gracias!

EDIT: Entiendo que olvidé un punto clave: la clase base es parte de un marco, y por lo tanto sería bueno no tener que modificarlo, si es posible.

+1

La pregunta es: * ¿Por qué necesita esta –

+0

* Y la respuesta es - no?. –

+0

por alguna razón no puedo volver a responder esta pregunta, creo que es apropiado agregar OOP como una etiqueta. – amit

Respuesta

12

Si el constructor de la clase base tiene al menos un argumento, se puede utilizar una función de ayuda de esta manera:

int DoStuffBeforeCtorAndForwardInt(int arg, Foo foo) 
{ 
    DoStuff(arg, foo); 
    return arg; 
} 

MyClass::MyClass(int arg, Foo foo) 
    : Base(DoStuffBeforeCtorAndForwardInt(arg, foo)) 
{ 
    // ... 
} 

Si desea default-inicializar la clase base , puede usar el copy-ctor para copiar una instancia de clase base inicializada por defecto:

Base DoStuffBeforeCtorAndReturnDefaultBase(int arg, Foo foo) 
{ 
    DoStuff(arg, foo); 
    return Base(); 
} 

MyClass::MyClass(int arg, Foo foo) 
    : Base(DoStuffBeforeCtorAndReturnDefaultBase(arg, foo)) 
{ 
    // ... 
} 

O Si Base no tiene que ser el primero de la clase base, se podría derivar MyClass de una clase de ayuda:

MyClass::MyClass(/* ... */) 
    : DoStuffHelperClass(/* ... */), 
    Base(/* ... */) 
{ 
    // ... 
} 

Todo lo anterior requieren que las "cosas" que haces no depende del objeto que se trata de para ser inicializado (es decir, las funciones no pueden ser funciones de miembros de manera segura y tampoco se puede pasar this como argumento para ellos).

Eso significa que puede hacer algo de registro o similar, pero también podría hacerlo una vez que se haya inicializado la clase base.

(EDITAR, excepto con la solución DoStuffHelperClass, por supuesto puede tener miembros en DoStuffHelperClass, acceder a ellos y lo que no)


Aunque tengo que decir que no puedo recordar haber consumido/necesitando/deseando algo así. Es bastante probable que haya otra solución (preferible) para lo que estás tratando de hacer.

+0

No sé si estas soluciones realmente funcionarán en lo que estoy tratando de hacer, pero de todos modos me parece que cada una de estas son excelentes respuestas a mi pregunta. ¡Gracias! –

0

no, porque no será seguro.
considera que tiene: una clase A y una variable A.var.
ahora considere B hereda de A, y usa var antes de que se haya inicializado A. ¡obtendrá un error de tiempo de ejecución! el lenguaje quiere evitar esto, por lo tanto, el constructor de la superclase debe inicializarse primero.

7

La clase base siempre está completamente construida antes de que comience la construcción de su propia clase. Si necesita hacer un cambio en el estado de la clase base, debe hacerlo explícitamente después de que se haya construido.

Ejemplo:

MyClass::MyClass() 
{ 
    // Implicit call to Base::Base() 

    int result = computeSomething(); 
    Base::setResult(result); 

    // ... 
} 
0

No, no puede hacerlo de esa manera, como otros han descrito en sus respuestas anteriores.

Su única posibilidad es la composición, OIA que MyClass utiliza Base clase como un campo de usuario:

class MyClass { 
public: 
    /** the methods... */ 

private: 
    Base* _base; 
}; 

por lo que se puede inicializar _base más tarde, cuando se tiene la información necesaria. No sé si esto puede aplicarse a su escenario, de todos modos.

+0

Desafortunadamente, creo que mi única posibilidad es crear una subclase real. El método que se invoca se pasa la instancia que estoy creando como parámetro, y allí necesito mi subclase. –

2

Además de las soluciones ya escritos, también se puede utilizar una función de constructor estático y hacer que el contructor de MyClass privado.

class QtBase{ 
    // ... 
}; 

class MyClass : public QtBase{ 
public: 
    // copy ctor public 
    MyClass(MyClass const& other); 

    static MyClass Create(/*args*/){ 
    // do what needs to be done 
    int idata; 
    float fdata; 
    // work with idata and fdata as if they were members of MyClass 
    return MyClass(idata,fdata); // finally make them members 
    } 

    static MyClass* New(/*args*/){ 
    int idata; 
    float fdata; 
    // work with idata and fdata as if they were members of MyClass 
    return new MyClass(idata,fdata); // finally make them members 
    } 

private: 
    // ctor private 
    MyClass(int a_idata, float a_fdata) 
    : idata(a_idata) 
    , fdata(a_fdata) 
    {} 

    int idata; 
    float fdata; 
}; 

Ahora usted tendría que crear instancias de MyClass ya sea como:

MyClass obj = MyClass::Create(/*args*/); 

o

MyClass* ptr = MyClass::New(/*args*/); 
+0

BTW: se llama el "Named Constructor Idiom";) –

+0

Si entiendo correctamente esto sería una solución si no tuviera que inicializar miembros de mi subclase. No puedo hacer eso con esta solución, ¿verdad? ¡Gracias! –

+0

@Luca: Bueno, siempre puedes tener un constructor que tome sus miembros ya inicializados.:) – Xeo

7

Uso del base-from-member idiom para ejecutar el código antes de la ctor de la base "real" clase (que es Base):

struct Base { 
    Base(string, int); 
}; 

struct DerivedDetail { 
    DerivedDetail() { 
    value = compute_some_value(); 
    value += more(); 
    value += etc(); 
    other = even_more_code(value); 
    } 
    string value; 
    int other; 
}; 

struct Derived : private DerivedDetail, Base { 
    Derived() : Base(value, other) {} 
    // In particular, note you can still use this->value and just 
    // ignore that it is from a base, yet this->value is still private 
    // within Derived. 
}; 

Esto funciona incluso si no tiene miembros reales que desee en DerivedDetail. Si proporciona más detalles sobre lo que debe hacer antes de la función de la Base, entonces puedo dar un mejor ejemplo.

+0

¡Sí! Esto es lo que estaba buscando, ¡gracias! ¡Voto ascendente! –

-1

No. No es posible, porque el orden de las llamadas al constructor está estrictamente definido por el estándar. El ctor de clase base debe ejecutarse antes de que se pueda ejecutar el ctor de clase derivada.

Cuestiones relacionadas