这是否会导致内存泄漏/我应该如何构建代码

发布于 2024-11-30 05:07:35 字数 2155 浏览 0 评论 0原文

我有点担心这段代码可能存在内存泄漏。我想知道是否确实存在泄漏,以及解决此问题的正确方法是什么。

描述:我有一个基类 Pet 和派生类 CatDogBird。我正在解析文件中的行,根据该行中的某些内容,我需要创建派生类的实例,然后以特定方式再次解析该行的一部分。 这是一个示例文件:

Dog Spot Brown,Labrador,5
Cat Felix Black,7
Bird Polly Green,Parrot,12,Crackers

和一些代码:

class Pet
{
protected:
  string _type;
  string _name;
  string _desc;

public:
  Pet();
  bool ParseLine(std::string line);
  string Type() { return _type; }
  string Name() { return _name; }
  string Desc() { return _desc; }
};

class Dog : public Pet
{
private:
  string _color;
  string _type;
  int _age;

public:
  Dog(string type, string name, string desc);
  bool ParseDesc(string desc);
};

主代码:

ifstream infile(filename, ifstream::in);
string line;
while(getline(infile, line))
{
   Pet* pet = new Pet();    // "new" called once
   if(pet->ParseLine(line))
   {
      if(pet->Type() == "Dog")
      {
         pet = new Dog(pet->Type(), pet->Name(), pet->Desc());   // "new" called again
         pet->ParseDesc(pet->Desc());
      }
      else if(pet->Type() == "Cat")
      {
         // ...
      }
   }
}

基本上发生的事情是这样的:

我从文件中取出一行并将其解析为三个字段(这就是 ParseLine() 所做的:

Type (Dog, Cat, Bird, etc.)
Name (Spot, Felix, Polly, etc.)
Description ("Brown,Labrador,5", "Black,7", "Green,Parrot,12,Crackers", etc)

然后,我将这三个字段分配给我的 Pet* 变量
。 然后,根据 Pet*->Type() 中的值,我解析 Pet*->Desc() 以获取该特定类型的附加信息动物。

我担心两次称接线员为“新”。我认为可能有一种更好的方法来格式化代码,可以完全避免这种情况。

我真的很想保留我的 getline() 例程。我不想查看该行以确定类型,然后决定如何创建我的实例。

另外,当我重新创建 Dog() 时,我必须重新分配变量 _type、_name 和 _desc,但我不想这样做。

谢谢。

--

具体来说,我该如何避免这种情况:

Pet* pet = new Pet();
pet->ParseLine(line);
string type = pet->Type();
string name = pet->Name();
string desc = pet->Desc();
delete pet;
if(type == "Dog")
{
   Pet* dog = new Dog(type, name, desc);
   dog->ParseDesc(desc);
}

I am slightly concerned that this code may have a memory leak. I would like to know if in fact there is a leak, and also what is the correct way to go about this problem.

Description: I have a base class Pet with derived classes Cat, Dog, and Bird. I am parsing lines from a file, and depending on certain contents in that line, I need to create an instance of the derived classes, and then parse part of the line again in a specific way.
Here is a sample file:

Dog Spot Brown,Labrador,5
Cat Felix Black,7
Bird Polly Green,Parrot,12,Crackers

And some code:

class Pet
{
protected:
  string _type;
  string _name;
  string _desc;

public:
  Pet();
  bool ParseLine(std::string line);
  string Type() { return _type; }
  string Name() { return _name; }
  string Desc() { return _desc; }
};

class Dog : public Pet
{
private:
  string _color;
  string _type;
  int _age;

public:
  Dog(string type, string name, string desc);
  bool ParseDesc(string desc);
};

Main Code:

ifstream infile(filename, ifstream::in);
string line;
while(getline(infile, line))
{
   Pet* pet = new Pet();    // "new" called once
   if(pet->ParseLine(line))
   {
      if(pet->Type() == "Dog")
      {
         pet = new Dog(pet->Type(), pet->Name(), pet->Desc());   // "new" called again
         pet->ParseDesc(pet->Desc());
      }
      else if(pet->Type() == "Cat")
      {
         // ...
      }
   }
}

Basically what happens is this:

I take a line from the file and parse it into three fields (this is what ParseLine() does:

Type (Dog, Cat, Bird, etc.)
Name (Spot, Felix, Polly, etc.)
Description ("Brown,Labrador,5", "Black,7", "Green,Parrot,12,Crackers", etc)

I then assign these three fields to my Pet* variable.
Then, according to the value in Pet*->Type(), I parse Pet*->Desc() to get the additional information for that particular type of animal.

I am worried about calling operator "new" twice. I think there is probably a better way to format the code that could avoid this altogether.

I would really like to keep my getline() routine. I do NOT want to peek at the line to determine the type, and then decide how to create my instance.

Also, I have to reassign my variables _type, _name, and _desc, when I recreate the Dog(), and I'd rather not have to do that.

Thanks.

--

Specifically, how do I avoid this:

Pet* pet = new Pet();
pet->ParseLine(line);
string type = pet->Type();
string name = pet->Name();
string desc = pet->Desc();
delete pet;
if(type == "Dog")
{
   Pet* dog = new Dog(type, name, desc);
   dog->ParseDesc(desc);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

尤怨 2024-12-07 05:07:35

是的,这会导致内存泄漏,因为您分配了一个永远不会被删除的 new Pet() ,并且指向它的指针被 new Dog() 或其他东西覆盖别的。

我建议您创建一个所谓的工厂函数,它从文件中读取一行,并创建该行状态的 Pet 类型。

Yes, this causes a memory leak, since you allocate a new Pet() which is never deleted and the pointer to it is overridden with either a new Dog() or something else.

I would suggest you create a so-called factory function, which reads a line from the file, and creates the type of Pet the line states.

姜生凉生 2024-12-07 05:07:35

是的,有泄漏。如果您不想手动释放指针,请使用一些智能指针库,例如 提升shared_ptr

至于代码中泄漏是如何发生的:当您再次调用 new 并分配给同一个指针时,就会发生泄漏。当您离开包含指针的作用域并且不释放内存时,就会发生另一次泄漏。

然而,你的整个设计对我来说有味道并且看起来不正确。您不应该创建宠物后才意识到它是狗或猫并重新创建它。您应该有一些“创建者”(工厂)对象来代替,它可以处理这个问题,并且 ParseLine 将是该工厂对象的成员,而不是 Dog 或 Pet 的成员。

Yes, there is a leak. If you do not want to release pointers manually, use some smart pointer library, e.g Boost shared_ptr.

As for how the leaks happen in your code: When you call new again and assign into the same pointer, you have a leak. When you leave the scope containing the pointer and do not release the memory, you have another leak.

However, your whole design smells to me and does not look right. You should not create a Pet only to realize later it is a Dog or a Cat and recreate it. You should have some "creator"(factory) object instead, which would handle this, and ParseLine would be a member of this factory object, not of the Dog or Pet.

你如我软肋 2024-12-07 05:07:35

除了此处的其他注释之外,您还需要在基类中使用虚拟析构函数,以确保在通过 Pet* 删除时正确清理派生类。

virtual ~Pet() {}

请参阅此处了解基本原理。

In addition to other comments here, you need a virtual destructor in the base class to ensure derived classes are cleaned up properly when deleting via a Pet*.

virtual ~Pet() {}

See here for rationale.

情深已缘浅 2024-12-07 05:07:35

这是我在类似情况下所做的。请注意,这不是唯一或最好的方法。

我正在从一个文件中读取随机长度的记录,其中每个记录都有一个公共的固定大小的标头,并且记录/对象的类型将根据标头信息确定。我创建了一个类似工厂的类,它读取标头,在容器中搜索匹配的条目,并使用工厂类/函数来创建所需的对象。标头数据被传递给新对象的构造函数进行复制。

在简单的伪代码中,它是这样的:

struct header_t
{
       int Type
       int Size
       ....
}

map<Type, CreateFunction> RecordFactory = 
{
      { TYPE1, CreateRecord1 },
      { TYPEX, CreateRecordX },
      ....
}

header_t NewHeader = ReadHeader()
RecordCreate = RecordFactory.Find(NewHeader.Type)
if (RecordCreate is valid) NewRecord = RecordCreate(NewHeader)

如果您有少量固定数量的类,那么您不一定需要复杂的工厂类,并且简短的 if/switch 列表也可以工作。如果您不熟悉工厂模式,请阅读它,因为它在多种情况下都很有用的情况。

Here is what I did in a similar situation. Note this is not the only or best way to do this.

I was reading random length records from a file where each record had a common fixed-sized header and the type of record/object would be determined from the header information. I created a factory-like class which read in the header, search a container for a matching entry and use a factory class/function to create the desired object. The header data was passed to the new object's constructor for copying.

In simple pseudo-code it was something like:

struct header_t
{
       int Type
       int Size
       ....
}

map<Type, CreateFunction> RecordFactory = 
{
      { TYPE1, CreateRecord1 },
      { TYPEX, CreateRecordX },
      ....
}

header_t NewHeader = ReadHeader()
RecordCreate = RecordFactory.Find(NewHeader.Type)
if (RecordCreate is valid) NewRecord = RecordCreate(NewHeader)

If you have a small, fixed number of classes then you don't necessarily need a complex factory class and a short if/switch list would work just as well. If you are not familiar with the Factory Pattern then read up on it as it is useful in a variety of situations.

甜警司 2024-12-07 05:07:35

我同意关于 boost::shared_ptr 的评论,学习如何使用它 - 它会让你的生活变得更好

看看你的“2 条新闻”解决方案,我现在明白你在问什么了。 ParseLine 函数不应该是 Pet 的实例方法(宠物不会解析线条,宠物会吠叫、吃东西、走路等)。

您应该有一个 PetFactory 类,它有一个静态方法 ParseLine 并返回一个多态 Pet* (或更好的 PetPtr)

I agree with comment about boost::shared_ptr, learn how to use it - it will make your life better

Looking at your '2 news' solution I now see what you are asking. THe ParseLine function should not be an instance method of Pet (A pet doesnt parse a line, a pet barks, eats, walks etc)

YOu should have a PetFactory class, that has a static method ParseLine and that returns a polymorphic Pet* (or better a PetPtr)

污味仙女 2024-12-07 05:07:35

经验法则始终是在调用 new 后删除。
如果(宠物)
删除宠物;

the rule of thumb is always to delete after you call new.
if(pet)
delete pet;

流绪微梦 2024-12-07 05:07:35

用于检测内存泄漏:-

  1. 有 purify 和 valgrind 等可用工具

  2. 当您无法访问这些工具时,有一个简单的方法
    将您怀疑存在内存泄漏的代码部分放入无限循环中。让程序在该循环中运行(可能需要一整夜左右)如果存在内存泄漏,内存将被耗尽并且新的分配将停止发生。如果您发现它运行顺利,请享受没有任何内存泄漏的情况。

希望这有帮助。

For detecting memory leaks:-

  1. There are tools available like purify and valgrind

  2. When you do not have any access to these tools, there is a simple method
    Put the section of code you are doubtful of having a memory leak in infinite loop. Let the program run in that loop (for may be an overnight or so) If there is a memory leak, memory will get finished and new allocations will cease to happen. If you find it running smooth, enjoy there is not any memory leak.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文