All the best.
I'm doing a short exercise and in principle it shouldn't give any more problems, but my faithful friends the segmentation fault are already here. The program is about managing a blog. What we are interested in is the composition relationship between an entry and its comments, which are pointers to an array, which in turn is an attribute of the entry:
#ifndef ENTRADA_H
#define ENTRADA_H
#include <string>
#include <stdexcept>
#include "Comentario.h"
class Entrada {
public:
Entrada();
Entrada(std::string titulo, std::string fecha, std::string texto);
Entrada(const Entrada& orig);
virtual ~Entrada();
void addComentario(Comentario *comentario);
void eliminarComentario(int cual);
std::string getTexto();
std::string getTitulo();
std::string getFecha();
int getNumComentarios();
Entrada& setTexto(std::string &texto);
Entrada& setTitulo(std::string &titulo);
Entrada& setFecha(std::string &fecha);
Comentario& getComentario (int cual);
static const int MAX_COMENTARIOS = 30;
private:
std::string _titulo = "";
std::string _texto = "";
std::string _fecha = "";
int _numComentarios = 0;
Comentario *_comentarios[MAX_COMENTARIOS];
};
#endif /* ENTRADA_H */
Logically, if an entry is deleted, the comments die with it, they no longer exist (it's a composition relationship). Well, the input destructor looks like this:
Entrada::~Entrada() {
for (int i = 0; i < _numComentarios; i++){ //Con la entrada se destruyen los comentarios
delete _comentarios[i];
}
}
_comments[] is filled in the main itself using the addComment method of the Input class :
void Entrada::addComentario(Comentario* comentario) {
if (_numComentarios == MAX_COMENTARIOS)
throw std::string ("Entrada::addComentario: No caben mas comentarios");
_comentarios[_numComentarios] = comentario;
_numComentarios++;
}
In the main it looks like this:
Entrada entrada1 ("Bienvenida", "1-1-2010", "Bienvenidos al nuevo blog");
Comentario com1 ("AAA", "Hola!!!", "3-1-2010");
entrada1.addComentario(&com1);
After which there are some cout and some try-catch that do behave as expected.
The problem? When reaching return 0 and going to end the program, when the destructors of the automatic objects are called, it is when the segmentation fault occurs , specifically when the input is going to be destroyed. The for loop is executed once (there is a comment), and in that execution the segmentation fault occurs when calling the Comment destructor , which is by default, without anything special (in that class the attributes are std::string objects without arrays or anything "special").
Now I'm considering the hypothesis that this occurred because the comment was destroyed before the input (or vice versa), always calling a destructor with no object to destroy, so I've tried creating them manually with new and also manually deleting them with delete and, in effect, the error no longer occurs (obviously I only do delete entrada1
, because the comment is already deleted there as well):
Entrada *entrada1 = new Entrada ("Bienvenida", "1-1-2010", "Bienvenidos al nuevo blog");
Comentario *com1 = new Comentario ("AAA", "Hola!!!", "3-1-2010");
entrada1->addComentario(com1);
//Resto del main sin mayor importancia
delete entrada1;
//delete com1 no se pone, pues ya lo ha eliminado el destructor de la entrada
return 0;
But of course, this already leads me to wonder: is it impossible to do it with automatic objects, since you can't control the call to the destructors?
Thanks. Greetings.
A segmentation fault can occur for several reasons:
To avoid these problems, great care must be taken with the requested memory and its life cycle; in the code you share apparently you are having it. But you could save yourself all the precautions if you followed certain programming principles:
It correctly defines which objects are responsible for memory.
Your object
Entrada
is conceptually incorrect because it takes over memory that does not belong to it: It is given a pointer to data whose origin is unknown, you will have problems if you pass a pointer to memory that is not dynamic, a null pointer, an incorrect pointer, a pointer to memory already freed or a pointer to an incorrect type:To avoid such nonsense, if an object is responsible for freeing memory, it should also be responsible for creating it, so the function should
Entrada::addComentario
NOT receive a pointer toComentario
but the data needed to create one:If, in addition, it is not necessary to manage objects
Comentario
outside ofEntrada
, the first object should be a private type of the second:Better yet, don't manage dynamic memory.
Your code doesn't seem like a case where dynamic memory is the solution, after all the number of comments is fixed. Why create them dynamically?
With this the need for destroyer disappears.
Other things to keep in mind.
_
), read this thread to find out why (although it was already mentioned in another question of yours).Entrada
, there's no need for the destructor to be virtual (you don't even need a destructor though).Member functions that read data are conceptually constant, mark them as such: