2013-10-08 36 views
1

我不断从编译器收到错误消息,说有内存泄漏,两个8字节块和一个5字节块。我已经在析构函数中删除了​​数组。析构函数如何处理范围?我的印象是,一旦主对象在超出范围时将被删除,就会被删除。如何阻止内存泄漏?

#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'。如果你坚持使用它,那么遵循[*三*规则](http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29),并且不要忘记'delete'所有你'新'。目前,它看起来不像“水果”是安全可复制或可分配的。 – juanchopanza

+2

在变量超出范围之前,您正在检查内存泄漏*。当然会有泄漏显示。但是,你又没有使用'delete'。 –

回答

3

似乎你的“泄漏”的主要来源是“水果”和“fruit1”指针 - _CrtDumpMemoryLeaks()用于存储检查尚未释放。在调用它之前,您需要删除这两点指向的数据。

这就是说,你还有其他问题。

C++没有自动垃圾收集。你必须跟踪和管理你的内存分配,或者使用类/代码来为你做。

考虑下面的代码:

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

此代码由分配,并将其存储在 “P”。尽管我们将这个值用于几个函数,但我们从未存储过它。所以,它永远不会返回到系统。泄漏守候在你的代码发生的

一个例子是在运营商>>

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

是的,在你的delete运算符,您可以从名称中删除[]。不过,只有处理您的代码到达〜果的情况下,可以考虑:

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::stringstd::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; 
}