Google Mock搭配unique_ptr使用时出现mock对象内存泄漏如何解决
问题根因
这个报错是GoogleTest的泄漏检测机制触发的:程序退出前你创建的MockDerivedClass实例从未执行析构,属于明确的mock对象生命周期问题,结合你贴的代码,触发原因基本是以下两类:
- 所有权接管逻辑错误:你虽然通过
std::move(mockClass)把mock对象的unique_ptr传给了CallingClass构造函数,但如果构造函数实现中没有将传入的unique_ptr移动赋值给成员变量drivedClass,甚至调用release()拿到裸指针后直接丢弃、没有在析构时手动释放,mock对象的内存就永远不会被回收,自然不会触发析构。 - 不完整类型析构的未定义行为:如果
CallingClass使用编译器自动生成的默认析构,且析构实例化的位置看不到DerivedClass/MockDerivedClass的完整定义,std::unique_ptr的默认删除器无法正确触发派生类的虚析构逻辑,最终导致Mock对象的析构不被执行,被gtest判定为泄漏。
另外你当前的代码设计本身有缺陷:CallingClass直接依赖具体实现类DerivedClass而非抽象基类Base,违反依赖倒置原则,后续替换mock、切换实现都会有不必要的耦合。
根本修复方案(推荐)
不要上来就用屏蔽报错的接口,按以下步骤调整可以彻底解决问题:
- 修正依赖关系,将
CallingClass的依赖从具体类DerivedClass改成抽象基类Base,本来基类已经定义了纯虚接口,完全不需要绑定具体实现:
构造函数实现通过初始化列表明确接管所有权,不要随意调用class CallingClass { public: CallingClass(std::unique_ptr<Base> drivedClass); void callSomthing(); // 你测试代码中调用了该方法,记得补声明 ~CallingClass(); private: std::unique_ptr<Base> drivedClass; };release()转裸指针:CallingClass::CallingClass(std::unique_ptr<Base> drivedClass) : drivedClass(std::move(drivedClass)) {} - 显式定义
CallingClass的析构,将析构实现放在能看到Base、DerivedClass、MockDerivedClass完整定义的编译单元中(比如测试文件、类对应的cpp文件),直接用默认实现即可:
这一步可以避免编译器在不完整类型上下文生成默认析构导致的unique_ptr删除UB。// 放在所有类完整定义之后 CallingClass::~CallingClass() = default; - 所有类定义末尾补全分号,你贴的三段类定义末尾都漏了分号,正常情况下这会直接导致编译失败,如果是贴代码时遗漏可忽略。
调整完成后,TEST用例执行到作用域结束时,栈上的callingClass会正常析构,内部持有的unique_ptr会逐层调用派生类的析构函数,Mock对象被正常回收,gtest的泄漏报错自然消失,同时mock上的期望调用校验也会在析构时正常执行。
临时屏蔽方案(不推荐)
如果你确实需要保留当前的所有权设计、暂时不修复泄漏,可以在创建mock对象后调用gtest提供的接口显式允许该对象泄漏,直接屏蔽报错:
TEST(TestGroup, Test1) { std::unique_ptr<MockDerivedClass> mockClass(new MockDerivedClass()); EXPECT_CALL(*mockClass, target_method()).WillRepeatedly(Return(1)); testing::Mock::AllowLeak(mockClass.get()); // 新增这行即可 CallingClass callingClass(std::move(mockClass)); callingClass.callSomthing(); // EXPECT_EQ处填入你实际需要断言的业务逻辑结果即可 }
注意这个方法只是关闭了对应mock对象的泄漏检查,并没有解决实际的内存泄漏问题,mock对象上的调用期望也不会在析构时被校验,仅适用于确实需要故意泄漏对象的特殊场景。
内容的提问来源于stack exchange,提问作者rajeevakarv
相关产品推荐
相关产品推荐

