使用C++异常的内存泄漏
Memory Leak using C++ Exceptions
我很难在C++11中编写一个小的文件句柄类。我知道STL中已经有很多东西可以实际处理文件,但出于学习目的,我想自己做这件事。
不幸的是,我似乎不明白,异常对C++程序的内存泄漏行为有什么影响,因为Valgrind告诉我,以下代码中有2个内存泄漏:
文件.h
#ifndef FILE_H
#define FILE_H
#include <iostream>
#include <memory>
#include <string>
#include <stdio.h>
class FileDeleter {
public:
void operator()(FILE *p);
};
class File {
public:
File(const std::string path);
private:
const std::string _path;
std::unique_ptr<FILE, FileDeleter> _fp;
};
#endif // FILE_H
文件.cpp
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <utility>
#include <stdio.h>
#include "file.h"
void FileDeleter::operator()(FILE *p)
{
if (!p)
return;
if (fclose(p) == EOF)
std::cerr << "FileDeleter: Couldn't close file" << std::endl;
}
File::File(const std::string path)
: _path(std::move(path)),
_fp(fopen(_path.c_str(), "a+"))
{
if (!_fp)
throw std::runtime_error("Couldn't open file");
}
main.cpp
#include <cstdlib>
#include "file.h"
int main()
{
File my_file("/root/.bashrc");
return EXIT_SUCCESS;
}
我确实故意选择打开/root/.bashrc,实际上是为了让File ctor抛出异常。如果我不扔到那里,瓦尔格里德会非常高兴。在使用异常时,我缺少什么?如何"正确"实现简单的文件句柄(异常安全)?
提前感谢!
注意:读/写操作仍然缺失,因为我已经在为基础知识而挣扎。
编辑#1:这是Valgrind的实际输出,使用--leak-check=full:
==7998== HEAP SUMMARY:
==7998== in use at exit: 233 bytes in 3 blocks
==7998== total heap usage: 5 allocs, 2 frees, 817 bytes allocated
==7998==
==7998== 38 bytes in 1 blocks are possibly lost in loss record 1 of 3
==7998== at 0x4C27CC2: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7998== by 0x4EEC4F8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x4EEDC30: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x4EEE047: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x40137D: main (in ../a.out)
==7998==
==7998== 43 bytes in 1 blocks are possibly lost in loss record 2 of 3
==7998== at 0x4C27CC2: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7998== by 0x4EEC4F8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x4EEDC30: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x4EEE047: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x400EBF: File::File(std::string) (in /home/frank/3Other/Code/Laboratory/c++/c++namedpipe/a.out)
==7998== by 0x401390: main (in ../a.out)
==7998==
==7998== 152 bytes in 1 blocks are possibly lost in loss record 3 of 3
==7998== at 0x4C27730: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7998== by 0x4E8F8F2: __cxa_allocate_exception (in /usr/lib/libstdc++.so.6.0.18)
==7998== by 0x400E9B: File::File(std::string) (in /home/frank/3Other/Code/Laboratory/c++/c++namedpipe/a.out)
==7998== by 0x401390: main (in ../a.out)
==7998==
==7998== LEAK SUMMARY:
==7998== definitely lost: 0 bytes in 0 blocks
==7998== indirectly lost: 0 bytes in 0 blocks
==7998== possibly lost: 233 bytes in 3 blocks
==7998== still reachable: 0 bytes in 0 blocks
==7998== suppressed: 0 bytes in 0 blocks
编辑#2:修复了Destructor中引发的异常。
编辑#3:删除了FileException类,改为使用std::runtime_error。
编辑#4:在删除程序中添加了NULL检查。
我看到以下问题:
- 不允许使用
fclose(NULL)
。。。- 但幸运的是,如果
get() == nullptr
,std::unique_ptr
不会调用deleter,所以这里应该可以
- 但幸运的是,如果
FileDeleter:operator()
是从析构函数调用的,这意味着它永远不应该抛出。在您的特定情况下,fclose返回的任何错误都将导致调用std::terminate
- 异常应该按值而不是引用存储字符串。该引用是指一个临时的,当您调用
what()
时,它将被销毁。- 正如Vlad&Praetorian指出,您可以删除有缺陷的代码,而不是修复它,只需让
std::runtime_error
为您处理即可。TBH,除非您计划向异常添加一些特定于文件的数据,或者您计划单独捕获文件异常,否则根本不需要此类
- 正如Vlad&Praetorian指出,您可以删除有缺陷的代码,而不是修复它,只需让
编辑:提供了valgrind输出,看起来堆栈展开从未完成。由于我最初对FileDeleter::operator()
被召唤并投掷的想法看起来是错误的,所以一个合理的测试是:如果你把一个尝试/接球放在主体内会发生什么?
一般说明:
- 永远不要在析构函数内部引发异常
- 永远不要从析构函数调用其他可能抛出的东西
因为如果在异常处理/堆栈展开期间调用析构函数,程序将立即终止。
相关文章:
- 从构造函数抛出异常时如何克服内存泄漏
- malloc() 可能出现内存泄漏
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 尽管遵循了规则,内存泄漏在哪里
- 为什么调用堆栈数组会导致内存泄漏
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 使用模板类的自定义列表类型中的内存泄漏
- 为什么以下C++代码中存在内存泄漏?
- OpenCV 我应该使用智能指针来防止内存泄漏吗?
- 我是否生成线程并导致内存泄漏?
- 多线程程序中出现意外的内存泄漏
- 为什么此函数会导致内存泄漏?
- 在 C++ 库中使用cythonized python时内存泄漏
- 需要帮助查找内存泄漏
- 瓦尔格林德的内存泄漏使用新的
- 无法找出我的代码中的内存泄漏
- C++ 结构内部的unordered_map会导致内存泄漏问题吗?
- 可视化 使用 VS Code 查找C++应用程序中的内存泄漏
- Shared_ptr双链接列表内存泄漏
- C++ 在类中使用常量引用文本时 O2 内存泄漏