Reputation: 1768
I am currently refactoring codes to make raw pointers use smart pointers and also making test for this class but stuck with an issue of a smart pointer being prematurely deleted
Here is an example:
class SomeObjectType
{
public:
void init()
{
}
};
class Helper
{
public:
Helper()
{
std::cout << "Helper constructor" << std::endl;
//some codes
}
~Helper()
{
std::cout << "Helper destructor" << std::endl;
//some codes
}
SomeObjectType* createObject()
{
return new SomeObjectType;
//some codes
}
void destroyObject(SomeObjectType* obj)
{
//some codes
}
};
class Base
{
public:
Base()
{
helper = std::make_shared<Helper>();
}
~Base()
{
destroyObject(obj);
}
void Init()
{
obj = createObject();
}
SomeObjectType* createObject()
{
return helper->createObject();
}
void destroyObject(SomeObjectType* obj)
{
helper->destroyObject(obj); // <-- I get the error here
}
protected:
std::shared_ptr<Helper> helper;
private:
SomeObjectType* obj;
FRIEND_TEST(BaseTest , Init_handleSuccess);
};
And in my test:
class BaseTest : public ::testing::Test
{
public:
void SetUp()
{
sut_ = std::make_unique<Base>();
helperMock_ = std::make_shared<HelperMock>();
//helperMock_ = new HelperMock; //it works when I dont use smart pointers but of course there is a leak here
}
protected:
std::unique_ptr<Base> sut_;
std::shared_ptr<HelperMock> helperMock_;
//HelperMock* helperMock_; //it works when I dont use smart pointers but of course there is a leak here
};
TEST_F(BaseTest, Init_handleSuccess)
{
auto obj = new SomeObjectType();
sut_->helper.reset(helperMock_.get());
EXPECT_EQ(S_OK, sut_->Init());
}
When I checked the logs, I see that this is what happens (in this order):
Test setup called
Base constructor called
Helper constructor called
Test teardown
Helper destructor called
Base destructor called
Base destructor tries to access helper
which was already deleted = crash!
So basically helper
pointer was already deleted prior to call of destroyObject()
because its life was tied with my test. So when I change the test to use a raw pointer for helper
, I do not get the double delete but then it becomes a leak since I didnt delete helper
at all :) What can I do so that helper
is still alive when the base class destructor is called?
Note: There is some API calls I need inside the helper's createObject
that is why I am mocking it. I can't inject helper as a dependency that is why I have to 'hack' it with sut_->helper.reset(helperMock_.get());
Upvotes: 0
Views: 1612
Reputation: 104589
I believe this might be your issue...
sut_->helper.reset(helperMock_);
I'm not even sure how that code above could possibly be compiling, because I thought that reset
expects a raw pointer for the shared_ptr to take ownership of. (And as you updated in the comments: you were really invoking reset with .get() ).
And the biggest mistake you can make with a shared_ptr is to invoke .get() or use *
operator - and pass the returned raw pointer to initialize another shared_ptr. Now you got two different reference counting sessions on the same object. Which ever set of shared_ptr instances goes away first will delete the object. Leaving the other set of shared_ptrs referencing an already deleted object.
Replace the above with just this:
sut_->helper = helperMock_;
I'm assuming HelperMock derives from Helper.
Upvotes: 2