代码之家  ›  专栏  ›  技术社区  ›  Leonardo

使用`unique_ptr`和`std::move`对象进行Google模拟,导致内存泄漏问题

  •  0
  • Leonardo  · 技术社区  · 4 月前

    这个问题是对 Google mock with unique_ptr object giving memory leak issue 我所参加考试的班级承担了所有的责任 unique_ptr 关于其建筑商:

    class Foo {
      public:
        virtual void doFoo() { std::cout << "Foo" << std::endl; }
    };
    
    class Bar {
      public:
        // So I can inject a mock Foo for testing
        Bar(std::unique_ptr<Foo> foo_)
            : foo_(std::move(foo_))
        {}
    
        Bar()
            : foo_(std::make_unique<Foo>())
        {}
    
        void doBar() { foo_->doFoo(); }
    
      private:
        std::unique_ptr<Foo> foo_;
    };
    
    class MockFoo : public Foo {
        MOCK_METHOD(void, doFoo, (), (override));
    };
    
    class FooTest : public ::testing::Test {
      public:
        void SetUp() override
        {
            mockFoo = std::make_unique<NiceMock<MockFoo>>();
        }
    
        std::unique_ptr<NiceMock<MockFoo>> mockFoo;
        NiceMock<MockFoo>* mockFooPtr;
    };
    
    TEST_F(FooTest, foo)
    {
        using ::testing::NiceMock;
    
        // This will fail on purpose
        EXPECT_CALL(*mockFoo, doFoo()).Times(2);
    
        mockFooPtr = mockFoo.get();
    
        Bar bar(std::move(mockFoo));
        bar.doBar();
    
        EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mockFooPtr));
    }
    

    在运行该测试时,我得到

    ERROR: this mock object (used in test FooTest.foo) should be deleted but never is. Its address is @0x5ccb272fba60.
    3: ERROR: 1 leaked mock object found at program exit. Expectations on a mock object are verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
    

    所以 VerifyAndClearExpectations 不起作用。问题是所有权转移。如果 Bar 更新以获取原始指针,没有模拟泄漏错误:

    class Bar {
      public:
        Bar(Foo* foo_)
            : foo_(foo_)
        {}
    
        void doBar() { foo_->doFoo(); }
    
      private:
        Foo* foo_;
    };
    
    TEST_F(FooTest, foo)
    {
        using ::testing::NiceMock;
    
    
        // This will fail on purpose
        EXPECT_CALL(*mockFoo, doFoo()).Times(1);
    
        mockFooPtr = mockFoo.get();
    
        // Bar bar(std::move(mockFoo));
        Bar bar(mockFoo.get());
        bar.doBar();
    }
    

    我必须使用 std::move 因为这就是生产代码所期望的。

    根据下面的评论,我尝试将测试更新为:

    TEST_F(FooTest, foo)
    {
        // This will fail on purpose
        EXPECT_CALL(*mockFoo, doFoo()).Times(2);
    
        mockFooPtr = mockFoo.get();
        {
            Bar bar(std::move(mockFoo));
            bar.doBar();
            EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mockFooPtr));
        }
    }
    

    我仍然会收到一个模拟泄漏错误。

    谢谢!

    1 回复  |  直到 4 月前
        1
  •  2
  •   JaMiT    4 月前

    多态对象应该有一个虚拟析构函数。如果不这样做,那么通过基类指针的所有权在销毁时会导致未定义的行为,因为只有基类析构函数会运行。特别是,使用您的代码,销毁 std::unique_ptr<Foo> 不调用 ~MockFoo() 即使托管对象实际上是 MockFoo .

    要获得所需的行为,请添加虚拟析构函数:

    class Foo {
      public:
        virtual ~Foo() = default; // <-- Add this line
    
        virtual void doFoo() { std::cout << "Foo" << std::endl; }
    };
    

    请参阅 When to use virtual destructors?