覆盖删除 [] 运算符
Overridding delete[] Operator?
>我目前正在实现一个基本的垃圾收集器,其目的是在程序结束时删除所有剩余的动态分配对象。我希望类文档更清楚地说明:
/**
* This basic garbage collector provides easy memory leak prevention.
* Just derive your class from utils::Object and the collector will
* care about freeing dynamically allocated objects. This basic
* implementation will just free all internal cached objects at the end
* of program. So allocated memory which was not freed manually will
* stay blocked up to this point.
*/
class GarbageCollector {
private:
// All object collector should care about (true means array)
std::map< Object*, bool > objects;
GarbageCollector();
/**
* Free left allocated memory.
*/
~GarbageCollector() {
std::map< Object*, bool >::iterator it = objects.begin();
int counter = 0;
while( it != objects.end() ) {
// save pointer and iterate to next because
// delete will remove obj from object set
Object* obj = it->first;
bool array = it->second;
++it;
++counter;
if( array ) {
delete[] obj;
}
else
delete obj;
}
if( counter )
std::cout << "GC: " << counter << " object(s) freedn";
}
public:
/**
* @return Static collector instance.
*/
static GarbageCollector& getCollector();
void addObject( Object* obj );
void addArray( Object* obj );
void remove( Object* obj );
};
其他类将从中继承的基类Object
会将分配内存的指针添加到此 gc:
class Object {
public:
void* operator new( std::size_t size ) {
void* ptr = malloc( size );
if( !ptr )
throw std::bad_alloc();
GarbageCollector::getCollector().addObject( static_cast<Object*>(ptr) );
return ptr;
}
void operator delete( void* ptr ) {
GarbageCollector::getCollector().remove( static_cast<Object*>(ptr) );
free( ptr );
}
void* operator new[]( std::size_t size ) {
void* ptr = malloc( size );
if( !ptr )
throw std::bad_alloc();
GarbageCollector::getCollector().addArray( static_cast<Object*>(ptr) );
return ptr;
}
void operator delete[]( void* ptr ) {
GarbageCollector::getCollector().remove( static_cast<Object*>(ptr) );
free( ptr );
}
};
这对于新语句工作正常。但是,如果尝试通过 new[] 分配数组,程序就会崩溃。Valgrind --leak-check=yes 给出了以下输出:
==3030== Invalid read of size 8
==3030== at 0x408305: utils::GarbageCollector::~GarbageCollector() (garbageCollector.cpp:40)
==3030== by 0x55A4820: __run_exit_handlers (exit.c:78)
==3030== by 0x55A48A4: exit (exit.c:100)
==3030== by 0x558A313: (below main) (libc-start.c:258)
==3030== Address 0x5b8e038 is 8 bytes before a block of size 144 alloc'd
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
==3030== Invalid free() / delete / delete[]
==3030== at 0x4C282E0: free (vg_replace_malloc.c:366)
==3030== by 0x409AE8: utils::Object::operator delete[](void*) (object.cpp:54)
==3030== by 0x408339: utils::GarbageCollector::~GarbageCollector() (garbageCollector.cpp:40)
==3030== by 0x55A4820: __run_exit_handlers (exit.c:78)
==3030== by 0x55A48A4: exit (exit.c:100)
==3030== by 0x558A313: (below main) (libc-start.c:258)
==3030== Address 0x5b8e038 is 8 bytes before a block of size 144 alloc'd
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
GC: 1 object(s) freed
==3030==
==3030== HEAP SUMMARY:
==3030== in use at exit: 144 bytes in 1 blocks
==3030== total heap usage: 8 allocs, 8 frees, 896 bytes allocated
==3030==
==3030== 144 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
==3030== LEAK SUMMARY:
==3030== definitely lost: 144 bytes in 1 blocks
==3030== indirectly lost: 0 bytes in 0 blocks
==3030== possibly lost: 0 bytes in 0 blocks
==3030== still reachable: 0 bytes in 0 blocks
==3030== suppressed: 0 bytes in 0 blocks
==3030==
==3030== For counts of detected and suppressed errors, rerun with: -v
==3030== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 4 from 4)
我调试了程序,gc 正在尝试删除 new[] 返回的地址上的内存。你能告诉我我的错在哪里吗?
不能将delete[]
表达式与从 operator new[]
返回的指针一起使用。您必须直接使用operator delete[]
。
这是因为new[]
表达式有时会调整operator new[]
的结果,而delete[]
表达式会向相反的方向调整参数,所以:
- 如果指针由
new[]
表达式返回,请使用delete[]
表达式释放它。 - 如果指针由调用
operator new[]
返回,则通过调用operator delete[]
释放它。
一般来说,对于new
表达/operator new
/delete
表达/operator delete
也是如此,但 GCC 让你侥幸逃脱。
这是一个技术答案,仅涉及崩溃。注释中讨论了代码作为内存泄漏防护工具的有用性。
更新 可以在 http://ideone.com/0atp5 找到论文的快速肮脏示例
请注意,如果您拨打operator delete[]
而不是delete[]
,
- 析构函数
- 不会被调用(但如果自动释放所有对象,则无论如何都不需要析构函数)
- 您无需将指针投射到
Object*
和返回,只需将所有内容存储为void*
即可。
>GarbageCollector::~GarbageCollector()
调用Object::operator[] delete
和Object::operator delete
,它们在迭代GarbageCollector::objects
映射时会更改它。尝试先制作副本:
...
std::map< Object*, bool > objectsCopy = objects;
std::map< Object*, bool >::iterator it = objectsCopy.begin();
int counter = 0;
while( it != objectsCopy.end() ) {
...
如果仅在程序结束时执行此操作,请禁止 remove() 调用以根本不删除它。这个对象将在一瞬间被摧毁,无论如何地图都将是空的。
- 编译"运算符删除"时C++编译器如何工作?
- 删除 x 与 ::运算符删除 (x)
- 未找到匹配的运算符删除;如果初始化引发异常,内存将不会被释放
- 为什么在运算符删除中不调用析构函数?
- 如何在 C++ 中使用删除运算符删除单个数据
- C++对自动(堆栈)指针使用运算符删除
- C++运算符删除覆盖并不总是使用
- 带大小参数和不带大小参数的"运算符删除":当两者都可用时,选择哪一个?
- C++ 运算符删除重载对派生类不起作用
- 无法覆盖C++中纯抽象类中的运算符删除/新建
- 对运算符删除覆盖的工作方式感到困惑
- C++/析构函数-运算符删除
- C++can运算符删除失败,如果不是原因
- 如果我写一个新的展示位置?我应该如何编写普通运算符删除
- 删除与运算符删除(和无效指针)
- 使用单个删除运算符删除多个指针
- 如何实现 C "classes" 的 C++11 冒名顶替者的运算符删除?
- shared_ptr-运算符删除中的访问冲突
- 使用delete运算符删除结构中的元素
- 内存管理 - 目标 C++ 运算符删除