Возможная утечка памяти? - PullRequest
       16

Возможная утечка памяти?

0 голосов
/ 20 февраля 2009

Хорошо, у меня есть два класса, назовите их A и B - в указанном порядке в коде. Класс B создает экземпляр класса A в виде массива, а класс B также имеет сообщение об ошибке char * variable, которое класс A должен установить в случае ошибки. Я создал третий класс с чисто виртуальной функцией для установки переменной errorMessage в B, а затем сделал B дочерним для этого третьего класса. Класс A создает указатель на третий класс, назовите его C - когда B инициализирует массив объектов A, он проходит через них и вызывает функцию в A, чтобы установить указатель A на C - он передает «this» этой функции , а затем A устанавливает указатель на C на «this», и поскольку C является родителем B, A может установить C-> errorMessage (мне пришлось сделать все это, потому что A и B не могли одновременно знать друг друга при компиляции время).

В любом случае, он работает нормально, и когда я передаю параметры командной строки в main (int, char **), он работает, если я не передам ему семь, восемь или более двенадцати параметров ... Я сузил его (через комментирование строк) на строку кода в A, которая устанавливает указатель на C, на значение, переданное ему B. Это не имело смысла для меня ... Я подозревал утечку памяти или что-то, но это кажется неправильным и я понятия не имею, как это исправить ... Также я не понимаю, почему конкретно семь, восемь и более двенадцати аргументов не работают, 1-6 и 9-12 работают нормально.

Вот мой код (урезанный) -

//class C
class errorContainer{

public:
    virtual ~errorContainer(){ }
    virtual void reportError(int,char*)=0;

};

//Class A
class switchObject{
    void reportError(int,char*);
    errorContainer* errorReference;

public:
    void bindErrorContainer(errorContainer*);
};

//Class A member function definitions
void switchObject::reportError(int errorCode,char* errorMessage){
    errorReference->reportError(errorCode,errorMessage);
}

void switchObject::bindErrorContainer(errorContainer* newReference){
    errorReference=newReference; //commenting out this line fixes the problem
}

//Class B
class switchSystem: public errorContainer{
    int errorCode;
    char* errorMessage;

public:
    switchSystem(int); //MUST specify number of switches in this system.
    void reportError(int,char*);
    int errCode();
    char* errMessage();
    switchObject* switchList;
};

//Class B member function definitions
switchSystem::switchSystem(int swLimit){
    int i;
    switchList=new (nothrow) switchObject[swLimit];
    for(i=0;i<swLimit;i++){
        switchList[i].bindErrorContainer(this);
    }
    errorCode=0;
    errorMessage="No errors.";
}

void switchSystem::reportError(int reportErrorCode,char* reportErrorMessage){
    int len=0,i;    
    errorCode=reportErrorCode;
    if(errorMessage){
        delete[] errorMessage;
    }
    while(reportErrorMessage[len]!='\0'){
        len++;
    }
    errorMessage=new char[len];
    for(i=0;i<=len;i++){
        errorMessage[i]=reportErrorMessage[i];
    }   
}

int switchSystem::errCode(){
    return errorCode;   
}

char* switchSystem::errMessage(){
    return errorMessage;    
}

Кто-нибудь знает, что я здесь не так сделал? Это выводит меня из себя ... Кажется, я не могу это исправить.

--- EDIT --- ладно, я настроил его так, как я могу использовать его в main ()

int main(int argc,char** argv){
   switchSystem sw (2)
   sw.switchList[0].argumentCount=2;
   sw.switchList[1].argumentCount=0;
   sw.switchList[0].identifier="a";
   sw.switchList[1].identifier="switch";
   sw.init(argc,argv);
   if(sw.errCode()>0){
      cout<< "Error "<< sw.errCode()<< ": "<< sw.errMessage()<< endl;
   }
}

Предполагается, что эта программа читает аргументы командной строки и обрабатывает определенные пользователем "переключатели" - например, как большинство программ командной строки обрабатывают переключатели, но вместо того, чтобы проверять их все в начале основного, я хотел написать класс и некоторые функции, чтобы сделать это для меня - создать объект switchSystem с количеством переключателей, установить их идентификаторы, независимо от того, принимают ли они аргументы или нет, а затем передать аргументы командной строки в init (), чтобы разобраться с ним , Затем проверить, как,

if(sw.isSet("switch")){ ... }
и т. Д.

Ответы [ 4 ]

5 голосов
/ 20 февраля 2009

Кажется, вам страшно:

  • Смешивать динамическую память со статическими строковыми константами («Без ошибок») в одном и том же указателе.
  • Используйте явный while -loop для вычисления длины строки; ты не слышал о strlen()?
  • Используйте такую ​​низкоуровневую C-подобную обработку строк, без веской причины ... Что не так с std::string?
  • Не правильно учитывает завершающий '\ 0' в строке, когда выделяет для него место и копирует его. Длина также не сохраняется, поэтому результирующий массив char довольно сложно интерпретировать.
4 голосов
/ 20 февраля 2009
  • reportError() должен быть объявлен виртуальным в switchSystem, как и в errorContainer.
  • char* вместо этого должно быть std::string, чтобы избежать всей этой ненужной работы.
  • Есть ли причина, по которой вы не можете использовать std::vector<switchObject> вместо new[]?
  • Вы не должны delete[] errorMessage, когда он указывает на статическую литеральную строку. Это приводит к неопределенному поведению. (Перевод: Bad Thing (TM).) ​​
  • Почему вы итеративно подсчитываете и копируете содержимое char*? Это напрашивается на неприятности. Вы ничего не делаете, чтобы защитить себя от вреда.
  • Почему switchObject должен передавать строку в switchSystem? Не лучше ли просто вернуть код ошибки или throw некоторый класс, производный от std::exception? Или, возможно, он должен отправить строку в глобальное средство ведения журнала?

Я думаю, что, возможно, вам следует переосмыслить свой дизайн, а не пытаться это исправить.

2 голосов
/ 20 февраля 2009

Все вышеприведенное сообщение содержит действительно хороший совет, если только это не домашнее задание и вы не можете использовать STL, я бы порекомендовал вам следовать им, ваши проблемы будут намного меньше.

Например, в этом фрагменте

errorMessage=new char[len];
for(i=0;i<=len;i++){
  errorMessage[i]=reportErrorMessage[i];
}

Вы выделили len байтов, но вы записываете в len + 1 байтов, вы не выделили память для нулевого терминатора '\ 0', перезапись памяти приводит к неприятным ошибкам, которые трудно отследить.

0 голосов
/ 20 февраля 2009

Я думаю, что утечка памяти - твоя второстепенная задача. Вам лучше бросить этот код и начать все заново с новым подходом. Это беспорядок.

...