这是否会导致内存泄漏/我应该如何构建代码
Does this cause a memory leak / How should I structure the code
我有点担心这段代码可能有内存泄漏。我想知道实际上是否有泄漏,以及解决这个问题的正确方法是什么。
描述:我有一个基类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()
例程。我不想偷看这一行来确定类型,然后决定如何创建我的实例。
另外,当我重新创建Dog()
时,我必须重新分配我的变量_type, _name, and _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);
}
是的,这会导致内存泄漏,因为您分配了一个永远不会删除的new Pet()
,并且指向它的指针被new Dog()
或其他东西覆盖。
我建议您创建一个所谓的工厂函数,它从文件中读取一行,并创建行状态的Pet
类型。
是的,有泄漏。如果你不想手动释放指针,可以使用一些智能指针库,例如Boost shared_ptr。
至于泄漏是如何在代码中发生的:当您再次调用new并对同一个指针进行赋值时,就会发生泄漏。当您离开包含指针的作用域而不释放内存时,就会发生另一次泄漏。
但是,你的整个设计对我来说很难闻,看起来也不对。你不应该创建一个宠物,后来才意识到它是一只狗或一只猫,然后重新创建它。您应该有一些"创建者"(工厂)对象来处理这个问题,并且ParseLine将是这个工厂对象的成员,而不是Dog或Pet的成员。
除了这里的其他注释之外,您还需要在基类中使用虚析构函数,以确保在通过Pet*
删除派生类时正确清理派生类。
virtual ~Pet() {}
下面是我在类似情况下所做的。注意,这不是唯一或最好的方法。
我正在从一个文件中读取随机长度的记录,其中每个记录都有一个固定大小的头,记录/对象的类型将从头信息确定。我创建了一个类似工厂的类,它读取头文件,在容器中搜索匹配的条目,并使用工厂类/函数来创建所需的对象。头数据被传递给新对象的构造函数进行复制。
在简单的伪代码中,它是这样的: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列表就可以了。如果您不熟悉工厂模式,请仔细阅读它,因为它在各种情况下都很有用。
我同意关于boost::shared_ptr的评论,学习如何使用它-它会让你的生活更美好
看看你的"2新闻"解决方案,我现在明白你在问什么了。ParseLine函数不应该是Pet的实例方法(宠物不会解析一行,宠物会叫、吃、走等)你应该有一个PetFactory类,它有一个静态方法ParseLine,并返回一个多态Pet*(或更好的PetPtr)
经验法则是在调用new后删除。
如果(pet)删除宠物;
用于检测内存泄漏:-
-
有可用的工具,如purify和valgrind
-
当您无法访问这些工具时,有一个简单的方法将您怀疑存在内存泄漏的代码段置于无限循环中。让程序在该循环中运行(可能是一个晚上左右)。如果存在内存泄漏,内存将被完成,新的分配将停止发生。如果你发现它运行平稳,享受没有任何内存泄漏。
希望这对你有帮助。
- C++为构建时间获取QDateTime的可靠方法
- 无法在 CLion 中构建 C++ 项目
- 函数向量_指针有不同的原型,我可以构建一个吗
- 如何使用ndk-build.cmd构建Android.so文件
- libssh 的函数在构建 libssh 时无法在 Qt 和 cmake 错误中找到
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 无法使用Qt Creator在Windows中构建yaml-cpp
- 构建一个由C和C++文件组成的库
- llvm构建器向基本块添加终止符
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- 如何跨平台将二进制资源构建到程序中?
- 将 OpenCV 与 CMAKE 中的项目一起构建为第三方库的正确方法
- 如何解决 Ninja c++ 构建和执行问题
- 使用 cmake 的 LLVM 构建在 tsan_libdispatch_mac.cc 期间失败; "Error: conflicting types for ..."
- CMake WxWidgets项目成功地在Linux上构建,但没有在Windows上构建
- 更改命令行 qt5 源代码构建配置的正确/快速方法
- 用CMake构建C++协议
- 通过增强构建构建Python扩展