una que programacion orientada objetos miembros metodo ejemplos codigo clases clase atributos c++ constructor initialization order-of-evaluation ctor-initializer

c++ - orientada - que es un metodo en programacion



clases dependientes como otros miembros de la clase (6)

Tengo una clase B que requiere que se construya una instancia de clase A :

class B { B(A* a); // there is no default constructor };

Ahora quiero crear una clase que contenga B como miembro, así que también necesito agregar A como miembro y proporcionarlo al constructor de B :

class C { C() : a(), b(&a) {} A a; // 1. initialized as a() B b; // 2. initialized as b(&a) - OK };

Pero el problema es que si alguien ocasionalmente cambia el orden de la definición de variables en la clase, se romperá

class C { C() : a(), b(&a) {} B b; // 1. initialized as b(&a) while "a" uninitialized A a; // too late... };

¿Hay una buena manera de resolver esto sin modificar las clases A y B ? Gracias.


¿Hay una buena manera de resolver esto sin modificar las clases A y B?

Active las advertencias del compilador ; para gcc, esto es -Wreorder (que está incluido en -Wall):

cc1plus: warnings being treated as errors t.cpp: In constructor ''A::A()'': Line 3: warning: ''A::y'' will be initialized after Line 3: warning: ''int A::x'' Line 2: warning: when initialized here

Alternativamente, use una herramienta similar a una pelusa que lo detecte.

Pero el problema es que si alguien ocasionalmente cambia el orden de la definición de variables en la clase ...

¿Por qué harían esto? Sospecho que te preocupas demasiado por lo que podría pasar. Aun así, puedes dejar un comentario en la clase:

A a; // Must be listed before member ''b''! B b;

No subestime la fuerza de los comentarios bien colocados. :) Luego, permita que alguien que deliberadamente los ignore obtenga lo que se merecen; estás usando C ++, después de todo.


Almacene b en unique_ptr, y configúrelo en el cuerpo, no en la lista de inicializadores:

class C { C() :a() { b = std::unique_ptr<B>(new B(&a)); } A a; std::unique_ptr<B> b; };


El problema es que te disparas en el pie con el tercer ejemplo. En C ++, importa el orden de las variables miembro en una clase / estructura. No importa cómo resuelva su problema en particular, si pasa datos no inicializados a un constructor debido al diseño deficiente de la clase / disposición del miembro, estará trabajando con datos unificados y posiblemente obtendrá un comportamiento indefinido, dependiendo del tipo de código implementado.

Para abordar su ejemplo particular, si B realmente requiere una A y la relación es uno a uno, ¿por qué no crear una nueva clase AB que tenga un objeto A y un objeto B en el orden correcto y pase la dirección de A a B ? Es decir:

class AB { public: AB():b_(&a_) {} private: A a_; B b_; };

ahora la clase C puede evitar el problema de ordenamiento utilizando AB lugar de A y B :

class C { public: ... private: AB ab_; };

Como se mencionó anteriormente, esto por supuesto supone una relación 1: 1 entre A y B Si un objeto A puede ser compartido por muchos objetos B , las cosas se vuelven más complicadas.


No estoy seguro de cuánto control tiene sobre la implementación y la estructura de C, pero ¿es necesario usar los objetos en la clase C? ¿Podría redefinir la clase para usar punteros en su lugar y luego moverlos de la lista de inicialización, por ejemplo,

class C { C() { a = new A; b = new B(a); } ~C() {delete a; delete b;} A* a; B* b; };

Esto evita el problema del orden en la declaración, pero le brinda la nueva cuestión de garantizar que se creen correctamente. Además, si crea MUCHAS C con mucha frecuencia, una lista de inicialización es un poco más rápida.


Una opción sería no almacenar explícitamente el A, sino utilizar la asignación dinámica para crear un nuevo A para almacenar en el B:

class C { public: C() : b(new A) { // handled in initialization list } private: B b; };

Como esto garantiza que la A se crea antes que la B, esto evitará que este problema ocurra.


Usa el conocido modismo de C ++ llamado Base-de-Miembro para resolver este problema.

Defina una clase base como,

class C_Base { A a; //moved `A a` to the base class! C_Base() : a() {} }; class C : public C_Base { C() : b(&a) {} B b; // 1. initialized as b(&a) while "a" uninitialized //A a; // too late... };

Ahora, a se garantiza que se inicializará antes b .