You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GoogleMock:跨引用Mock类引发对象泄漏的解决方法

解决Mock对象循环引用导致的内存泄漏问题

问题背景

现有Server与Client两个接口类,各自对应MockServer和MockClient实现。需求是在MockServer中添加clients成员变量,让getClients()方法返回该变量以方便测试时动态修改,但当前实现中,MockServer通过shared_ptr持有MockClient,MockClient又通过shared_ptr持有MockServer,形成循环引用,导致测试结束后Mock对象无法被正常销毁,出现内存泄漏。

原代码示例

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vector>
#include <memory>

struct Client;
struct Server
{
  virtual const std::vector<std::shared_ptr<Client>>& getClients() = 0;
};
struct Client
{
  virtual std::shared_ptr<Server> getServer() = 0;
};

struct MockServer : virtual Server
{
  MOCK_METHOD0(getClients, const std::vector<std::shared_ptr<Client>>&());
  std::vector<std::shared_ptr<Client>> clients;  // 需要保留的变量

protected:
  MockServer()
  {
    ON_CALL(*this, getClients()).WillByDefault(testing::ReturnRef(this->clients));
  }
  ~MockServer()
  {
    clients.clear();
    std::cout << __func__ << " - This is never printed." << std::endl;
  }

public:
  static std::shared_ptr<MockServer> create()
  {
    return std::make_shared<testing::NiceMock<MockServer>>();
  }
};
struct MockClient : virtual Client
{
  MOCK_METHOD0(getServer, std::shared_ptr<Server>());

protected:
  MockClient(std::shared_ptr<Server> server)
  {
    ON_CALL(*this, getServer()).WillByDefault(testing::Return(server));
  }
  ~MockClient()
  {
    std::cout << __func__ << " - This is never printed." << std::endl;
  }

public:
  static std::shared_ptr<MockClient> create(std::shared_ptr<Server> server)
  {
    return std::make_shared<testing::NiceMock<MockClient>>(server);
  }
};

TEST(ExampleOfLeak, testLeakedMockObjects)
{
  auto server = MockServer::create();
  auto client1 = MockClient::create(server);
  auto client2 = MockClient::create(server);
  EXPECT_EQ(server, client1->getServer());
  EXPECT_EQ(server, client2->getServer());
  EXPECT_TRUE(server->getClients().empty());
  server->clients.push_back(client1);
  server->clients.push_back(client2);
  EXPECT_EQ(client1, server->getClients().front());
  EXPECT_EQ(client2, server->getClients().back());
  // server->clients.clear(); 手动清理可避免泄漏,但无法在所有测试中手动执行
}

原运行结果

[==========] 运行1个测试用例中的1个测试。
[----------] 全局测试环境初始化。
[----------] ExampleOfLeak中的1个测试
[ RUN      ] ExampleOfLeak.testLeakedMockObjects
[       OK ] ExampleOfLeak.testLeakedMockObjects (0 毫秒)
[----------] ExampleOfLeak中的1个测试(总耗时0毫秒)

[----------] 全局测试环境清理
[==========] 1个测试用例中的1个测试执行完毕。(总耗时0毫秒)
[  PASSED  ] 1个测试。

foo.cc:43: 错误:此Mock对象(用于测试ExampleOfLeak.testLeakedMockObjects)应被销毁但未被销毁。其地址为@0x2fc3170。
foo.cc:43: 错误:此Mock对象(用于测试ExampleOfLeak.testLeakedMockObjects)应被销毁但未被销毁。其地址为@0x2fcbcb0。
foo.cc:22: 错误:此Mock对象(用于测试ExampleOfLeak.testLeakedMockObjects)应被销毁但未被销毁。其地址为@0x2fd6570。
错误:程序退出时发现3个泄漏的Mock对象。

解决方案(不修改原接口及create函数)

核心是打破循环引用:让MockClient内部用weak_ptr保存Server引用,而非shared_ptr,同时保持getServer()返回shared_ptr的接口要求。

修改后的MockClient代码

struct MockClient : virtual Client
{
  MOCK_METHOD0(getServer, std::shared_ptr<Server>());

protected:
  // 内部用weak_ptr存储Server,避免强引用
  std::weak_ptr<Server> server_weak;

  MockClient(std::shared_ptr<Server> server) : server_weak(std::move(server))
  {
    // 默认行为中把weak_ptr升级为shared_ptr返回,符合原接口
    ON_CALL(*this, getServer()).WillByDefault(testing::Invoke([this]() {
      return server_weak.lock();
    }));
  }
  ~MockClient()
  {
    std::cout << __func__ << " - This is now printed!" << std::endl;
  }

public:
  static std::shared_ptr<MockClient> create(std::shared_ptr<Server> server)
  {
    return std::make_shared<testing::NiceMock<MockClient>>(server);
  }
};

同时调整MockServer的析构(可选,但更稳妥)

可以去掉clients.clear(),因为循环打破后,server销毁时clients里的shared_ptr会自动释放:

~MockServer()
{
  // clients.clear(); 不再需要手动清理
  std::cout << __func__ << " - This is now printed!" << std::endl;
}

原理说明

  • MockClient用weak_ptr持有Server,不会增加Server的引用计数,仅保留一个非拥有性的引用。
  • 当测试结束时,server的shared_ptr被销毁,其引用计数减为0,MockServer会被正常析构,同时clients里的MockClient的shared_ptr也会被释放,MockClient的引用计数减为0,MockClient也会被正常析构。
  • 完全保留了原接口的返回值类型,也没有修改create函数的签名和逻辑,满足需求。

内容的提问来源于stack exchange,提问作者Fred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:31:13