我有点担心这段代码可能有内存泄漏。我想知道实际上是否存在泄漏,以及解决此问题的正确方法是什么。
描述:我有一个基类Pet
,其派生类Cat
,Dog
和Bird
。我正在解析文件中的行,并且根据该行中的某些内容,我需要创建派生类的实例,然后以特定方式再次解析行的一部分。
这是一个示例文件:
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()
以获取该特定类型动物的其他信息。
我担心两次调用操作符“new”。我认为可能有一种更好的格式化代码的方法可以完全避免这种情况。
我真的想保留getline()
例程。我不想查看该行以确定类型,然后决定如何创建我的实例。
另外,当我重新创建_type, _name, and _desc
时,我必须重新分配我的变量Dog()
,而我宁愿不必这样做。
感谢。
-
具体来说,我该如何避免这种情况:
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);
}
答案 0 :(得分:8)
是的,这会导致内存泄漏,因为您分配的new Pet()
永远不会被删除,而指向它的指针会被new Dog()
或其他内容覆盖。
我建议您创建一个所谓的工厂函数,它从文件中读取一行,并创建Pet
行状态的类型。
答案 1 :(得分:3)
是的,有泄漏。如果您不想手动释放指针,请使用一些智能指针库,例如Boost shared_ptr。
关于代码中泄漏的发生方式:当你再次调用new并分配到同一个指针时,就会发生泄漏。当您离开包含指针的范围并且不释放内存时,您会再次发生泄漏。
然而,你的整个设计闻起来让我看起来不对劲。你不应该创造一个宠物只是为了以后意识到它是一只狗或猫并重新创建它。你应该有一些“创造者”(工厂)对象,它会处理这个,而ParseLine将是这个工厂对象的成员,而不是狗或宠物。
答案 2 :(得分:2)
答案 3 :(得分:1)
以下是我在类似情况下所做的事情。请注意,这不是唯一或最好的方法。
我正在从一个文件中读取随机长度记录,其中每个记录都有一个共同的固定大小的标题,并且记录/对象的类型将根据标题信息确定。我创建了一个类似工厂的类,它在标题中读取,在容器中搜索匹配的条目,并使用工厂类/函数来创建所需的对象。标题数据被传递给新对象的构造函数以进行复制。
在简单的伪代码中,它类似于:
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列表也可以。如果您不熟悉Factory Pattern,请阅读它,因为它在各种情况下都很有用。
答案 4 :(得分:1)
我同意关于boost :: shared_ptr的评论,了解如何使用它 - 它会让你的生活更美好
看看你的“2新闻”解决方案,我现在看到你在问什么。 ParseLine函数不应该是Pet的实例方法(宠物不解剖线条,宠物吠叫,吃东西,走路等)
你应该有一个PetFactory类,它有一个静态方法ParseLine,它返回一个多态Pet *(或者更好的PetPtr)
答案 5 :(得分:0)
在您拨打新电话后,经常会删除经验法则 如果(PET) 删除宠物;
答案 6 :(得分:0)
用于检测内存泄漏: -
有一些工具,如purify和valgrind
当您无权访问这些工具时,有一种简单的方法 把你怀疑在无限循环中存在内存泄漏的代码部分。让程序在该循环中运行(可能是一夜之间)如果存在内存泄漏,内存将完成,新的分配将不再发生。如果您发现它运行平稳,请享受没有任何内存泄漏。
希望这会有所帮助。