metodos implementa herencia ejemplos definicion constructores como codigo clases c++

implementa - Llamar a constructores en c++ sin nuevo



herencia c++ (7)

A menudo he visto que las personas crean objetos en C ++ usando

Thing myThing("asdf");

En lugar de esto:

Thing myThing = Thing("asdf");

Esto parece funcionar (usando gcc), al menos mientras no haya plantillas involucradas. Mi pregunta ahora, ¿es correcta la primera línea y, de ser así, debería usarla?


Ambas líneas son de hecho correctas, pero sutilmente hacen cosas diferentes.

La primera línea crea un nuevo objeto en la pila llamando a un constructor del formato Thing(const char*) .

El segundo es un poco más complejo. Básicamente hace lo siguiente

  1. Crea un objeto de tipo Thing usando el constructor Thing(const char*)
  2. Cree un objeto de tipo Thing usando la Thing(const Thing&) del constructor Thing(const Thing&)
  3. Call ~Thing() en el objeto creado en el paso # 1

El compilador bien puede optimizar el segundo formulario en el primer formulario, pero no tiene por qué.

#include <iostream> class A { public: A() { std::cerr << "Empty constructor" << std::endl; } A(const A&) { std::cerr << "Copy constructor" << std::endl; } A(const char* str) { std::cerr << "char constructor: " << str << std::endl; } ~A() { std::cerr << "destructor" << std::endl; } }; void direct() { std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl; A a(__FUNCTION__); static_cast<void>(a); // avoid warnings about unused variables } void assignment() { std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl; A a = A(__FUNCTION__); static_cast<void>(a); // avoid warnings about unused variables } void prove_copy_constructor_is_called() { std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl; A a(__FUNCTION__); A b = a; static_cast<void>(b); // avoid warnings about unused variables } int main() { direct(); assignment(); prove_copy_constructor_is_called(); return 0; }

Salida de gcc 4.4:

TEST: direct char constructor: direct destructor TEST: assignment char constructor: assignment destructor TEST: prove_copy_constructor_is_called char constructor: prove_copy_constructor_is_called Copy constructor destructor destructor


En adición a la respuesta de JaredPar

1-usual ctor, 2nd-function-like-ctor con objeto temporal.

Compila esta fuente en algún lugar aquí http://melpon.org/wandbox/ con diferentes compiladores

// turn off rvo for clang, gcc with ''-fno-elide-constructors'' #include <stdio.h> class Thing { public: Thing(const char*){puts(__FUNCTION__ );} Thing(const Thing&){puts(__FUNCTION__ );} ~Thing(){puts(__FUNCTION__);} }; int main(int /*argc*/, const char** /*argv*/) { Thing myThing = Thing("asdf"); }

Y verás el resultado.

De ISO / IEC 14882 2003-10-15

8.5, parte 12

Su primera y segunda construcción se llaman inicialización directa

12.1, parte 13

Se puede usar una conversión de tipo de notación funcional (5.2.3) para crear nuevos objetos de su tipo. [Nota: la sintaxis se parece a una llamada explícita del constructor. ] ... Un objeto creado de esta manera no tiene nombre. [Nota: 12.2 describe la vida útil de los objetos temporales. ] [Nota: las llamadas de constructor explícitas no arrojan valores l, véase 3.10. ]

Dónde leer sobre RVO:

12 Funciones de miembros especiales / 12.8 Copia de objetos de clase / Parte 15

Cuando se cumplen ciertos criterios, una implementación puede omitir la construcción de copia de un objeto de clase, incluso si el constructor de copia y / o el destructor del objeto tienen efectos secundarios .

Desactívelo con el indicador de compilación del comentario para ver dicho comportamiento de copia)


Idealmente, un compilador optimizaría el segundo, pero no es obligatorio. El primero es la mejor manera. Sin embargo, es muy importante entender la distinción entre stack y heap en C ++, ya que debes administrar tu propia memoria Heap.


Jugué un poco con él y la sintaxis parece ser bastante extraña cuando un constructor no toma argumentos. Déjame dar un ejemplo:

#include <iostream> using namespace std; class Thing { public: Thing(); }; Thing::Thing() { cout << "Hi" << endl; } int main() { //Thing myThing(); // Does not work Thing myThing; // Works }

así que simplemente escribiendo Thing myThing w / o brackets realmente llama al constructor, mientras que Thing myThing () hace que el compilador crea un puntero a la función o algo así ?? !!


Simplemente, ambas líneas crean el objeto en la pila, en lugar de en el montón como lo hace ''nuevo''. La segunda línea en realidad implica una segunda llamada a un constructor de copia, por lo que debe evitarse (también debe corregirse como se indica en los comentarios). Debe usar la pila para objetos pequeños tanto como sea posible, ya que es más rápido, sin embargo, si sus objetos van a sobrevivir durante más tiempo que el marco de pila, entonces es claramente la opción incorrecta.


Supongo que con la segunda línea realmente quieres decir:

Thing *thing = new Thing("uiae");

que sería la forma estándar de crear nuevos objetos dinámicos (necesarios para el enlace dinámico y el polimorfismo) y almacenar su dirección en un puntero. Su código hace lo que JaredPar describió, es decir, crear dos objetos (uno pasó un const char* , el otro pasó a const Thing& ), y luego llama al destructor ( ~Thing() ) en el primer objeto ( const char* one).

Por el contrario, esto:

Thing thing("uiae");

crea un objeto estático que se destruye automáticamente al salir del alcance actual.