Creación de objetos en C ++

Soy un progtwigdor de C que intenta comprender C ++. Muchos tutoriales demuestran ejemplificación de objetos usando un fragmento como:

Dog* sparky = new Dog(); 

lo que implica que más tarde harás:

 delete sparky; 

que tiene sentido Ahora, en el caso en que la asignación de memoria dinámica no es necesaria, ¿hay alguna razón para usar lo anterior en lugar de

 Dog sparky; 

y dejar que se llame al destructor una vez que Sparky sale del scope?

¡Gracias!

Por el contrario, siempre deberías preferir las asignaciones de stack, en la medida en que, como regla general, nunca deberías tener new / delete en tu código de usuario.

Como dices, cuando la variable se declara en la stack, se llama automáticamente a su destructor cuando se sale del scope, que es tu herramienta principal para rastrear el tiempo de vida del recurso y evitar fugas.

Entonces, en general, cada vez que necesita asignar un recurso, ya sea memoria (llamando a new), manejadores de archivos, sockets o cualquier otra cosa, envuélvalo en una clase donde el constructor adquiera el recurso y el destructor lo libere. Luego puede crear un objeto de ese tipo en la stack, y se le garantiza que su recurso se liberará cuando salga del scope. De esta manera, no tiene que seguir sus pares nuevos / eliminar en todas partes para asegurarse de evitar memory leaks.

El nombre más común para este idioma es RAII

También busque en las clases de punteros inteligentes que se utilizan para ajustar los punteros resultantes en los raros casos en los que tiene que asignar algo nuevo fuera de un objeto RAII dedicado. En su lugar, pasa el puntero a un puntero inteligente, que luego rastrea su duración, por ejemplo, mediante recuento de referencias, y llama al destructor cuando la última referencia sale del scope. La biblioteca estándar tiene std::unique_ptr para la std::unique_ptr simple basada en el scope, y std::shared_ptr que hace referencia al recuento para implementar la propiedad compartida.

Muchos tutoriales demuestran ejemplificación de objetos usando un fragmento como …

Entonces, lo que has descubierto es que la mayoría de los tutoriales apestan. 😉 La mayoría de los tutoriales le enseñan malas prácticas de C ++, incluida la invocación de nuevas / eliminar para crear variables cuando no es necesario, y le dificultan el seguimiento de la vida de sus asignaciones.

Aunque tener cosas en la stack podría ser una ventaja en términos de asignación y liberación automática, tiene algunas desventajas.

  1. Es posible que no desee asignar objetos grandes en la stack.

  2. Envío dynamic! Considera este código:

 #include  class A { public: virtual void f(); virtual ~A() {} }; class B : public A { public: virtual void f(); }; void A::f() {cout << "A";} void B::f() {cout << "B";} int main(void) { A *a = new B(); a->f(); delete a; return 0; } 

Esto imprimirá “B”. Ahora veamos qué pasa cuando usamos Stack:

 int main(void) { A a = B(); af(); return 0; } 

Esto imprimirá “A”, que puede no ser intuitivo para aquellos que están familiarizados con Java u otros lenguajes orientados a objetos. La razón es que ya no tienes un puntero a una instancia de B En cambio, se crea una instancia de B y se copia a a variable de tipo A

Algunas cosas pueden pasar de manera poco intuitiva, especialmente cuando eres nuevo en C ++. En C tienes tus punteros y eso es todo. Usted sabe cómo usarlos y SIEMPRE son iguales. En C ++, este no es el caso. Imagínese lo que sucede, cuando utiliza un en este ejemplo como argumento para un método: las cosas se vuelven más complicadas y SIGNIFICA una gran diferencia si a es de tipo A o A* o incluso A& (llamada por referencia). Muchas combinaciones son posibles y todas se comportan de manera diferente.

Bueno, la razón para usar el puntero sería exactamente la misma que la razón para usar punteros en C asignados con malloc: ¡si quieres que tu objeto viva más tiempo que tu variable!

Incluso se recomienda NO usar el nuevo operador si puede evitarlo. Especialmente si usas excepciones. En general, es mucho más seguro dejar que el comstackdor libere sus objetos.

He visto este antipatrón de gente que no acaba de obtener el operador & address-of. Si necesitan llamar a una función con un puntero, siempre asignarán en el montón para que obtengan un puntero.

 void FeedTheDog(Dog* hungryDog); Dog* badDog = new Dog; FeedTheDog(badDog); delete badDog; Dog goodDog; FeedTheDog(&goodDog); 

Trata el montón como un bien inmueble muy importante y úsalo con mucha prudencia. La regla básica es utilizar la stack siempre que sea posible y usar el montón cuando no haya otra forma. Al asignar los objetos en la stack puede obtener muchos beneficios, tales como:

(1) No necesita preocuparse por el desenrollado de la stack en caso de excepciones

(2) No necesita preocuparse por la fragmentación de la memoria provocada por la asignación de más espacio del necesario por su administrador de montón.

La única razón por la que me preocuparía es que Dog ahora está asignado en la stack, en lugar de en el montón. Entonces, si Dog tiene un tamaño de megabytes, puede tener un problema,

Si necesita ir a la ruta nueva / eliminar, desconfíe de las excepciones. Y debido a esto, debe usar auto_ptr o uno de los tipos de puntero inteligente para administrar la vida útil del objeto.

No hay ninguna razón para lo nuevo (en el montón) cuando puedes asignar en la stack (a menos que por alguna razón tengas una stack pequeña y quieras usar el montón.

Es posible que desee considerar el uso de un shared_ptr (o una de sus variantes) de la biblioteca estándar si desea asignar en el montón. Eso manejará hacer la eliminación una vez que todas las referencias a shared_ptr hayan desaparecido.

Existe un motivo adicional, que nadie más ha mencionado, por el que puede optar por crear su objeto dinámicamente. Los objetos dynamics basados ​​en stacks te permiten utilizar el polymorphism .

Tuve el mismo problema en Visual Studio. Tienes que usar:

yourClass-> classMethod ();

más bien que:

yourClass.classMethod ();