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
4

3 回答 3

3

您的“泄漏”的主要来源似乎是“fruit”和“fruit1”指针 - _CrtDumpMemoryLeaks() 检查尚未释放的内存。您需要在调用之前删除这两个指向的数据。

也就是说,您还有其他问题。

C++ 没有自动垃圾收集。您必须跟踪和管理您的内存分配或使用为您执行此操作的类/代码。

考虑以下代码:

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

该代码进行了分配,并将其存储在“p”中。尽管我们将这个值用于几个函数,但我们从未存储它。因此,它永远不会返回到系统中。

在您的代码中等待发生泄漏的一个示例是在 operator>>

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

是的,在您的删除运算符中,您从名称中删除 []。但这仅处理您的代码到达〜Fruit的情况,请考虑:

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

此时,您不再存储原始值,也没有删除它。

你真正遇到的问题是你正在使用一种非常类似于 C 的方法。如果你打算用 C++ 编写这个,你应该考虑使用RAII,例如你可以使用std::unique_ptr类。

TL;DR不要暴露原始指针,将它们封装在确保它们超出范围或重新分配给它们时被释放的东西后面。

于 2013-10-08T05:35:06.400 回答
2

您的水果对象超出范围时不会被删除。相反,它们会泄漏。因为这是你的程序做的最后一件事,所以没有真正的后果,一旦程序退出,操作系统将回收内存。尽管如此,这是一个泄漏。

为什么不直接在堆栈上创建它们呢?

于 2013-10-08T05:20:26.337 回答
1

我不会在您的示例中解决我已经在评论中完成的特定内存问题,而是通过使用std::stringand完全避免它们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;
}
于 2013-10-08T05:33:37.117 回答