const char* в моем классе имеет ненужные символы после того, как он вернулся из функции

Учебный класс:

class myclass {
  public:
    myclass(void);

    const char* server;

  private:
    char pidchar[6];
    int pidnum;

};

Функция

myclass parseINI(const char* file)
{
    myclass iniOptions;
    CSimpleIniA ini;
    ini.SetUnicode();
    ini.LoadFile(file);
    const char* server = ini.GetValue("", "server", "");
    iniOptions.server = server;
    std::cout << server << "\n"; // Prints the correct value here
    fflush(stdout);
    return iniOptions;


}

Вызов его из основной функции

int _tmain(int argc, TCHAR* argv[])
{

 myclass options;
 options = parseINI("myapp.ini");
 std::cout << options.server << "\n"; // It prints junk here
 return 0;
}

Что я сделал не так?

7 ответов

Решение

const char* вернулся GetValue() вероятно, принадлежал к ini объект. Когда вы вышли из parseIni() функция, ini вышел из области видимости и был уничтожен, что может означать, что ваш указатель больше не действителен.

Попробуйте использовать std::string для server тип элемента вместо const char*,

Похоже, вы используете память, которая освобождается, когда CSimpleIniA выходит за рамки в parseINI,

const char* server = ini.GetValue("", "server", "");
iniOptions.server = server;

Скопируйте значение, которое возвращается в новый блок памяти, прежде чем вернуться из parseINI функция.

string server = ini.GetValue("", "server", "");
iniOptions.server = new char[server.length() + 1];
std::copy(server.begin(), server.end(), iniOptions.server);         
iniOptions.server[server.length()] = 0;

Я предполагаю, что время жизни данных, на которые указывает char* вернулся из CSimpleIniA::GetValue() такой же, как CSimpleIni сам объект Так когда ini уничтожен, указатель возвращается из GetValue() становится недействительным. (Я никогда не использовал CSimpleIni и не достаточно внимательно изучал документы, чтобы точно знать, но именно на это указывает поведение).

Я бы предложил изменить myclass::server быть std:string Объект и установить его, используя что-то вроде:

iniOptions.server = std::string(server);

который даст myclass::server возражать это собственная копия строковых данных.

const char* server = ini.GetValue("", "server", "");

Это значение выходит за пределы области действия, когда функция завершается, поэтому, когда вы присваиваете значение этого указателя указателю сервера вашего объекта, место в памяти, на которое они указывают, освобождает память из стека в конце функции, и затем его настигли другие вещи.

Использование std::string или даже просто char[] будет предпочтительным, чтобы просто решить проблему с наименьшим количеством изменений, так как они будут назначены фактическим значением, а не местом в памяти, как указатели.

Что вы действительно должны сделать, так это посмотреть ссылочную прозрачность. Это предотвратит повторение подобных проблем

То, как вы используете класс как функцию, возвращает тип данных в C++, совершенно неверно. В C++ существует 2 типа данных: тип значения, ссылочный тип. класс принадлежит второму; Из функции вы можете вернуть данные типа значения или указатель любых данных. Но вы не можете перенастроить объект ссылочного типа. Потому что объект ссылочного типа будет освобожден сразу после того, как код выйдет из области, в которой определен объект.

Вы можете сделать любым способом:

1: определить parseINI как:

     myclass* parseINI(const char* file) 
     {     
           myclass* iniOptions = new myclass();
           ........
           return iniOptions;   
      } 

и затем используйте это так:

      myclass* options = parseINI("myapp.ini"); 

2: определить parseINI как:

       void parseINI(myclass& options, const char* file) 
        {     
           ........//asigne value to options's members
        } 

и затем используйте это так:

        myclass options;
        parseINI(options,"myapp.ini"); 

3: Сделайте то, что вы сделали, но добавьте метод присвоения (operator=) в myclass

iniOptions располагается в стеке и удаляется автоматически при возврате функции. Вы должны распределить его по куче, используя new()

Проблема в том, что локальная переменная server указывает на символьный буфер, возвращаемый ini.GetValue(), который разрушается при paraseINI() возвращается.

Один из способов исправить это - выделить новый буфер самостоятельно и скопировать символы.

const char* server = ini.GetValue("", "server", "");
int length = strlen(server) + 1;  // length of the string +1 for the NULL character.
delete [] iniOptions.server; // free the old buffer
iniOptions.server = new char[length]; // allocate your own buffer
strncpy(iniOptions.server, server, length); // copy the characters

Чтобы это работало, нужно сделать myclass::server неконстантный, и вы должны инициализировать его в NULL в конструкторе и удалить его в деструкторе.

Лучшим способом справиться с этой ситуацией будет использование std::string вместо char * за muclass::server, Сюда std::string позаботится об управлении памятью для вас, и код будет безопасным для исключений.

Если вы делаете muclass::server std::stringтогда вы просто делаете

const char* server = ini.GetValue("", "server", "");
iniOptions.server = std::string(server);

И вам не нужно ничего с этим делать в конструкторе или деструкторе.

Другие вопросы по тегам