2013-10-08 2 views
1

Я продолжаю получать сообщения об ошибках от компилятора, говоря, что есть утечка памяти, два 8-байтовых блока и один 5-байтовый блок. Я уже удалил массив *name в деструкторе. Как дескриптор дескриптора? У меня сложилось впечатление, что после того, как основной закончатся, объекты fruit будут удалены, поскольку они вышли из сферы действия.Как остановить утечку памяти?

#include "Fruit.h" 
#include "LeakWatcher.h" 

using namespace std; 
Fruit::Fruit(const Fruit &temp) 
{ 
    name = temp.name; 
    for(int i = 0; i < CODE_LEN - 1; i++) 
    { 
     code[i] = temp.code[i]; 
    } 
} 
void Fruit::operator=(const Fruit &tempFruit) 
{ 
    name = tempFruit.name; 
    for(int i = 0; i < CODE_LEN; i++) 
    { 
     code[i] = tempFruit.code[i]; 
    } 

} 
Fruit::~Fruit() 
{ 
    delete[] name; 

} 
bool Fruit::operator==(const Fruit &tempFruit) 
{ 
    int i = 0; 
    while(name[i] != NULL && tempFruit.name[i] != NULL) 
    { 
     if(name[i] != tempFruit.name[i]) 
      return false; 
     i++; 
    } 
    if(name[i] != NULL || tempFruit.name[i] != NULL) 
     return false; 
    return true; 
} 
bool Fruit::operator<(const Fruit &tempFruit) 
{ 
    int i = 0; 
    while(name[i] != NULL && tempFruit.name[i] != NULL) 
    { 
     if((int)name[i] < (int)tempFruit.name[i]) 
      return true; 
     else if((int)name[i] > (int)tempFruit.name[i]) 
      return false; 
     i++; 
    } 
    if(name[i] == NULL && tempFruit.name[i] != NULL) 
     return true; 
    else 
     return false; 
} 
std::ostream & operator<<(std::ostream &os, const Fruit *printFruit) 
{ 
    os << setiosflags(ios::left) << setw(MAX_NAME_LEN) << printFruit->name << " "; 
    for(int i = 0; i < CODE_LEN; i++) 
    { 
     os << printFruit->code[i]; 
    } 
    os << endl; 
    return os; 
} 

std::istream & operator>>(std::istream &is, Fruit *readFruit) 
{ 

    string tempString; 
    is >> tempString; 
    int size = tempString.length(); 
    readFruit->name = new char[tempString.length()]; 
    for(int i = 0; i <= (int)tempString.length(); i++) 
    { 
     readFruit->name[i] = tempString[i]; 
    } 
    readFruit->name[(int)tempString.length()] = '\0'; 
    for(int i =0; i < CODE_LEN; i++) 
    { 
     is >> readFruit->code[i]; 
    } 
    return is; 
} 
void stuff() 
{ 

} 
void main() 
{ 
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); 
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); 
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT); 
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); 
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); 
    Fruit *fruit = new Fruit(); 
    Fruit *fruit1 = new Fruit(); 
    cin >> fruit; 
    *fruit1 = *fruit; 
    cout << fruit << fruit1; 
    _CrtDumpMemoryLeaks(); 

} 

H

#ifndef _FRUIT_H 
#define _FRUIT_H 
#include <cstring> 
#include <sstream> 
#include <iomanip> 
#include <iostream> 
enum { CODE_LEN = 4 }; 
enum { MAX_NAME_LEN = 30 }; 
class Fruit 
{ 
private: 
    char *name; 
    char code[CODE_LEN]; 
public: 
    Fruit(const Fruit &temp); 
    Fruit(){name = NULL;}; 
    bool operator<(const Fruit &other); 
    friend std::ostream & operator<<(std::ostream &os, const Fruit *printFruit); 
    bool operator==(const Fruit &other); 
    bool operator!=(const Fruit &other){return!(*this==other);}; 
    friend std::istream & operator>>(std::istream& is, Fruit *readFruit); 
    void Fruit::operator=(const Fruit &tempFruit); 
    ~Fruit(); 
}; 
#endif 
+2

Возможно, вам нужно удалить 'fruit' и' fruit1'? Также обратите внимание, что вы присваиваете 'fruit'' fruit1', что означает, что вы никогда не сможете освободить второй объект. –

+5

Простой: прекратите использование 'new' всюду. Если вы настаиваете на его использовании, следуйте правилу [* of three *] (http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29) и не забудьте «удалить» все, что вы «новое». На данный момент это не похоже на то, что «Фрукты» безопасно переписываются или присваиваются. – juanchopanza

+2

Вы проверяете утечки памяти * до того, как * переменные выходят из области видимости. Конечно, будут обнаружены утечки. Но опять же вы не используете 'delete'. –

ответ

3

Представляется, что основным источником вашей «утечки» является «плод» и «fruit1» указатель - _CrtDumpMemoryLeaks() проверяет на память, которая не была освобождена. Вам необходимо удалить данные, указанные этими двумя пунктами, прежде чем вы вызовете их.

У этого есть другие проблемы.

C++ не имеет автоматической сборки мусора. Вы должны либо отслеживать и управлять выделениями памяти, либо использовать классы/код, которые это делают для вас.

Рассмотрим следующий код:

void iNeedALeak() { 
    void* p = new char[64]; 
    strcpy(p, "Hello world"); 
    std::cout << p << std::endl; 
} 

Этот код сделал распределение, и хранить его в «р». Несмотря на то, что мы использовали значение для нескольких функций, мы никогда не сохраняли его. И поэтому он никогда не возвращается в систему.

Одним из примеров утечки ожидающей произойдут в вашем коде в операторе >>

std::istream & operator>>(std::istream &is, Fruit *readFruit) 
{ 
    string tempString; 
    is >> tempString; 
    int size = tempString.length(); 
    readFruit->name = new char[tempString.length()]; 

Да, в вашем удалении оператора, удалить [] от имени. Но это только обрабатывает случай, когда ваш код достигает ~ Фрукты, считают:

Fruit f; 
cin >> f; // readFruit->name = new char[].. 
cin >> f; // readFruit->name = new char[]... 

В этот момент, ты не-дольше хранить исходное значение, и вы не удалите его.

На самом деле проблема, с которой вы сталкиваетесь, заключается в том, что вы используете очень C-подобный подход. Если вы планируете написать это на C++, вы должны рассмотреть возможность использования RAII, например, вы можете использовать класс std::unique_ptr.

TL; DR Не выставляйте необработанные указатели, инкапсулируйте их за то, что гарантирует их освобождение, когда они выходят из сферы действия или когда они переназначаются.

2

Ваши плодовые объекты не будут удалены, когда они выходят из области видимости. Вместо этого они будут течь. Поскольку это последнее, что делает ваша программа, нет никаких реальных последствий, и ОС вернет память после выхода программы. Тем не менее, это утечка.

Почему бы просто не создать их в стеке?

1

Вместо того, чтобы обращаться к конкретным проблемам памяти в вашем примере, которые я уже сделал в комментариях, я бы полностью их избегал, используя std::string и std::array. Ваш код закипит до

#include <iostream> 
#include <string> 
#include <array> 
enum { CODE_LEN = 4 }; 
class Fruit 
{ 
private: 
    std::string name; 
    std::array<char,CODE_LEN> code; 
public: 
    bool operator<(const Fruit &other); 
    bool operator==(const Fruit& other); 
    bool operator!=(const Fruit& other) {return!(*this==other);} 
    friend std::ostream& operator<<(std::ostream &os, const Fruit& f); 
    friend std::istream& operator>>(std::istream& is, Fruit& f); 
}; 

Обратите внимание: никаких конструкторов, задающих оператор или деструктор не указаны. Некоторые из операторов также могут быть значительно упрощены, например.

bool Fruit::operator==(const Fruit& rhs) 
{ 
    return code == rhs.code; 
} 

bool Fruit::operator<(const Fruit& rhs) 
{ 
    return name < rhs.name; 
} 
Смежные вопросы