Problema con asignación de string C++

Iniciado por xRodak, 30 Enero 2014, 19:50 PM

0 Miembros y 1 Visitante están viendo este tema.

xRodak

Hola compañeros ! He buscado y buscado acerca del error que poseo, y solo he logrado averiguarque es referente a la inicialización de variables y/o punteros, pero no he logrado corregirlo.

El error que poseo es el siguiente:

std::string::assign(std::string const&) ()

Según tiene que ver con la inicialización de mis clases, por lo que les dejo más o menos lo que poseo:

Account& account = *getContext().account;
account.createCharacter(Professions::Bloodge, *getContext().textures);
Character& character = account.getCharacter(account.getSlot());
character.setName(name); //<------ AQUÍ EL ERROR


Les explico como funciona:

'getContext.account' me regresa un puntero a un objeto Account. Este objeto YA FUE INICIALIZADO por lo que dicho puntero no debería tener fallos.
'account.createCharacter' crea un objeto Character nuevo en la cuenta.
'account.getCharacter' regresa una referencia a dicho personaje creado (la cuenta posee 3 Slots, por lo que se señala cual de los 3 personajes quiero obtener, con el getSlot, que devuelta el Slot actual en el cual estoy creando el personaje, para asegurarme de que obtenga el personaje recien creado, y no una referencia vacía u otro Character de los demás slots, evitando errores)

Guardo la referencia al personaje recién creado en la variable 'character'. Y luego procedo a asignarle un nombre. La variable 'name' contiene un string, no vacio (std::string name;)


Account.hpp

class Account
{
public:
    Account();
    void createCharacter(Professions::ID id, const TextureHolder& textures);
    Character& getCharacter(int n);
private:
    std::array<Character*, MAX_CHARACTERS> mCharacters;    //array que contiene punteros a Character, de tamaño MAX_CHARACTERS
    int currentChars;
    int currentSlot;
};


Account.cpp

Account::Account()
{
    currentChars = 0;
    currentSlot = -1;

    //Aquí señalo que inicialmente todos los punteros a objetos Character, serán punteros nulos, es decir, una cuenta sin personajes (Characters)
    mCharacters[0] = nullptr;
    mCharacters[1] = nullptr;
    mCharacters[2] = nullptr;
}

void Account::createCharacter(Professions::ID id, const TextureHolder& textures)
{
        //crea un nuevo personaje en un puntero
        std::unique_ptr<Character> newCharacter( new Character(id, textures));

        //hace que el array apunte a este nuevo Character creado, para asi guardar el nuevo personaje en la cuenta
        //currentSlot es el Slot en donde estoy creando el personaje, la función getSlot utilizada arriba devuelve este valor.
        mCharacters[currentSlot] = newCharacter.get();

        ++currentChars;

}

Character& Account::getCharacter(int n)
{
    std::cout << "n = " << n << std::endl;

    // retorna un personaje de la cuenta (retorna una referencia)
    return *mCharacters[n];
}


Character.hpp

class Character:
{
    public:
explicit Character(Professions::ID id, const TextureHolder& textures);
void setName(std::string& name);
std::string getName() const;

    private:
        std::string mName;

};


Character.cpp


Character::Character(Professions::ID id, const TextureHolder& textures)
{
}

void Character::setName(std::string& name)          //<------- Esta es la función que da el error
{
    std::cout << "setName activada" << std::endl;
    mName = name;                                              //<------- Esta es la linea del error
    std::cout << "Se dio el nombre con exito" << std::endl; //Obviamente esta linea no se imprime en la consola S:
}


std::string Character::getName() const
{
    return mName;
}




Es bastante código, pero el funcionamiento es super simple y fácil de entender, coloqué todo lo involucrado para que se entienda de donde puede provenir el error.

Espero que puedan ayudarme, estoy bastante desesperado, mucha gracias de antemano !

eferion

estás seguro de que todos los accesos a memoria son correctos??

xRodak

A que te refieres con accesos de memoria?

Saludos.

xRodak

Solucionado !

Después de mucho leer en internet, intenté nuevamente averiguar cuál era el error. Como sospechaba, al crear un Character, este era creado exitosamente, pero al ser devuelto por la funcion getCharacter, su memoría era liberada, por lo que la variable 'character' utilizaba memoria liberada.

Sinceramente, no logro ver el error por mi mismo, sé que el error es gracias a utilizar el std::unique_ptr, pero no logro ver en qué linea o porqué la memoria es liberada al momento de utilizar la función getCharacter.

Si alguien pudiera explicarme esto último le estaría muy agradecido, para no volver a cometer el mismo error a futuro que tanto me costó corregir.

Muchas gracias por leer. Saludos.

PD: Perdón por el doble post, pero era para que la gente que ya vió el tema entre nuevamente para ayudarme con este nuevo error, si lo editaba esas visitas probablemente se perderían al no tener actividad el tema.

eferion

#4
Vale, no había visto lo del unique_ptr... la próxima vez la etiqueta del código déjala algo tal que [code = cpp] sin espacios... se verán mejor estas cosas.

A ver, el unique_ptr es un puntero inteligente que tiene la particularidad de que no puede ser compartido, es decir, solo puede existir una referencia al objeto apuntado por el mismo.

El siguiente programa ilustra lo que digo:

Código (cpp) [Seleccionar]


class POO
{
 public:
   POO( )
   { std::cout << "POO::POO( );" << std::endl; }

   ~POO( )
   { std::cout << "POO:~POO( );" << std::endl; }
};

void func( POO* poo )
{
 std::unique_ptr< POO > ptr( poo );
}

void main( )
{
 POO* objeto = new POO;

 func( poo );

 std::cout << "la instancia apuntada por objeto aqui ya no existe" << std::endl;
 std::cout << "la siguiente llamada provocara un error" << std::endl;
 func( poo );
}


Como se puede comprobar, la primera llamada a poo provoca la destrucción del objeto, ya que unique_ptr sale de su ámbito y, al destruirse, se provoca la eliminación del objeto apuntado por poo.

Una cosa que sí que puede hacer unique_ptr es pasar el objeto a otro unique_ptr, de tal forma que el primer unique_ptr perderá la referencia.

Obviamente, se puede tener acceso al puntero "crudo" gestionado por unique_ptr, bien con la función get( ), bien con el operador asterisco. Sin embargo recuperar y almacenar ese puntero en tu código tiene sus peligros, ya que si el puntero inteligente se destruye también desaparecerá el objeto apuntado por tu puntero crudo... lo que pasa es que tú no te vas a enterar.

Otra opción que tienes es usar shared_ptr... lo que pasa es que en este punto tienes que tener cuidado, pues si bien shared_ptr permite tener varias copias del puntero inteligente, puede provocar lagunas de memoria si dos shared_ptr acaban apuntándose mutuamente... es decir:

Si necesitas devolver "copias" de un shared_ptr, la mejor opción es usar weak_ptr, que supone una suerte de enlace "debil" al shared_ptr. Con weak_ptr puedes extender el uso del objeto gestionado por shared_ptr por todo el sistema de una forma sencilla.

weak_ptr tiene la ventaja de que permite saber si el objeto apuntado sigue existiendo o no, lo que te permite evitar accesos no autorizados a memoria.

Código (cpp) [Seleccionar]

class POO
{
 public:
   shared_ptr< POO > referencia;

   POO( )
   { std::cout << "POO::POO( );" << std::endl; }

   ~POO( )
   { std::cout << "POO:~POO( );" << std::endl; }
};

void func( )
{
 shared_ptr< POO > ptr1( new POO );
 shared_ptr< POO > ptr2( new POO );

 ptr1->referencia = ptr2;
 ptr2->referencia = ptr1;
}

void main( )
{
 func( );

 std::cout << "llegados a este punto no hay llamadas a los destructores" << std::endl;
}


Lo dicho, los punteros inteligentes son una herramienta bastante poderosa... pero hay que tener cuidado con el diseño.

xRodak

Muchas gracias eferion ! Sinceramente me aún me cuesta entender entender un poco lo del shared_ptr y el leak_ptr. Me dedicaré a estudiarlos más a fondo para entender bien su funcionamiento y saber cual debo utilizar para mis objetivos.

Hasta el momento entendi muy bien por qué se pierde la memoria utilizando un unique_ptr. Muchas gracias nuevamente por tu respuesta !

Saludos.