错误代码
class classA{
private:
std::string input;
public:
std::string callback_method(std::string input){return input;};
classA(std::string in):input(in){};
};
typedef std::string (CAPTURE::*Callback_from_classA)(std::string); // 声明回调成员函数指针
class classB{
private:
Callback_from_classA callback;
std::shared_ptr<classA> CA;
public:
// 传入的是&CA的指针对象,然后std::move给std::shared_ptr<classA>,这玩意自动回收和class析构冲突,会delete ClassA CA
classB(Callback_from_classA cb_A, *p_classA):callback(cb_A),CA(std::move(p_classA)){};
}
int main()
{
classA CA("INPUT_STR");
// 传入的是&CA的指针对象
classB CB(&classA::callback_method, &CA);
}
运行时报错
deinit app ok!
free(): invalid pointer
Aborted
valgrind调试
==1286== Invalid free() / delete / delete[] / realloc()
==1286== at 0x484B6C0: operator delete(void*, unsigned long) (vg_replace_malloc.c:593)
==1286== by 0x11360B: std::_Sp_counted_ptr<CAPTURE*, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:380)
==1286== by 0x112923: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:158)
==1286== by 0x112023: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:733)
==1286== by 0x111E9F: std::__shared_ptr<CAPTURE, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:1183)
==1286== by 0x111EBF: std::shared_ptr<CAPTURE>::~shared_ptr() (shared_ptr.h:121)
==1286== by 0x111F67: IPC_FIFO::~IPC_FIFO() (CapturePhotoApp.cc:198)
==1286== by 0x11187F: main (CapturePhotoApp.cc:437)
==1286== Address 0x1fff000278 is on thread 1's stack
==1286== in frame #7, created by main (CapturePhotoApp.cc:419)
问题分析
来自chagpt
:
这段Valgrind日志显示了内存管理问题,包括:
- 无效的释放操作:Invalid free() / delete / delete[] / realloc,发生在CAPTURE对象的析构过程中。该问题通常意味着试图释放一个已经释放或未分配的内存块,或错误地使用栈内存的地址进行释放操作。根据日志中信息,地址在main函数创建的栈中(栈地址0x1fff000278),这表示可能在堆上和栈上对对象进行了不一致的释放操作。
- 潜在内存泄漏:大量记录显示glib和gobject库的malloc、calloc或realloc函数分配的内存未被释放,显示为“possibly lost”。这可能是因为这些库内部的数据结构在应用程序退出时并未完全释放,或者是由未正确释放的指针引起的。
解决建议
- 检查CAPTURE对象的创建与销毁:如果使用new创建的对象需使用delete释放,若是shared_ptr则确保没有其他副本引用该对象。另外,确保没有对栈对象(自动变量)错误使用delete或free。
- 调试glib/gobject库内存分配问题:这些库可能未释放它们内部的内存。如果这是预期行为,可以忽略,但如果泄漏不可忽视,可以尝试:
- 确保使用适当的清理函数,例如g_object_unref来释放glib和gobject的对象引用。
- 检查是否有未完成的异步操作,或在应用程序退出时未适当清理的资源。
- 排查程序生命周期中的共享资源:一些资源可能由于多线程或异步调用未被清理,建议使用valgrind的--trace-children=yes选项获取更全面的资源分配信息。
进一步分析
- 栈上的对象被尝试释放
报告指出 CAPTURE 对象的地址位于主线程的栈上,但是尝试调用 delete 来释放它。在 C++ 中,只有通过 new 动态分配的对象才可以使用 delete 释放,而栈上的对象不应使用 delete。这种错误通常发生在误用智能指针(例如 std::shared_ptr 或 std::unique_ptr)来管理栈上对象时。
解决方法: 确保 CAPTURE 对象是在堆上分配的(使用 new),如果确实需要用智能指针管理的话。否则,避免对栈上对象使用智能指针。
# 智能指针应该生而智能指针,死而自动收回
class classA{
private:
std::string input;
public:
std::string callback_method(std::string input){return input;};
classA(std::string in):input(in){};
};
typedef std::string (CAPTURE::*Callback_from_classA)(std::string); // 声明回调成员函数指针
class classB{
private:
Callback_from_classA callback;
std::shared_ptr<classA> CA;
public:
// 直接使用智能指针进行传递,资源会自动管理和回收,不会发生拷贝。
classB(Callback_from_classA cb_A, std::shared_ptr<classA> p_classA):callback(cb_A),CA(p_classA){};
}
int main()
{
// 直接创建智能指针,自动回收
std::shared_ptr<classA> CA = std::make<classA>("INPUT_STR");
// 智能指针传递,直接传入参数
classB CB(&classA::callback_method, CA);
}
std::move()是否可以不用
- 是否可以不用 std::move():
如果你想在 classB 和 main 中的 CA 都共享相同的 std::shared_ptr<classA>,那么 不使用 std::move() 是完全可以的,因为 std::shared_ptr 的拷贝会共享资源。
如果你不打算在 classB 中保留对 CA 的引用(即不再需要 main 中的 CA),那么使用 std::move() 更加合适。std::move() 会避免不必要的拷贝开销,将资源的所有权转移到 classB 中。
- 什么时候应该使用 std::move():
使用 std::move() 的场景一般是:
- 你希望转移资源的所有权,避免拷贝。
- 你不再需要原始对象,或者你希望避免不必要的拷贝。
总结:
- 不使用 std::move() 是可以的,特别是因为 std::shared_ptr 的拷贝是安全的,且会共享资源。
- 使用 std::move() 会将 CA 中的资源转移到 classB 中,避免不必要的拷贝。
- 是否使用 std::move() 取决于你对资源管理的需求,通常如果你不需要在原对象(main 中的 CA)中再使用资源,或者希望将其传递给 classB 并放弃在 main 中的控制权,使用 std::move() 会更合适。