Search code examples
c++pointersstructpimpl-idiom

Why am I getting garbage values when I print the value?


I'm trying to learn about the PIMPL idiom, and c++ in general.

I have a class with a PIMPL style interface, that sets an int value to 7. But, I'm getting a garbage value when I print it, and I don't understand why.

Code

Test.cpp

#include <iostream>
#include "Test.h"
struct Foo::Bar
{
    int value;
};

Foo::Foo()
{
    Bar tempBar;
    myBar = &tempBar;
    myBar->value = 7;
}
void Foo::printValue()
{
    std::cout << "Value = " << myBar->value << std::endl;
}
int main()
{
    Foo myFoo;
    myFoo.printValue();
    return 0;
}

Test.h

class Foo
{
    private:
        struct Bar;
        Bar* myBar;
    public:
        Foo();
        void printValue();
        //~Foo();
};

Output

Value = 2147120498


Solution

  • mybar is a pointer to a local variable inside the Foo constructor. When the constructor exits, the variable is gone, but myBar is still pointing at the old memory.

    Since you want to implement PIMPL, there is really only 1 choice. You need to dynamically allocate myBar using new, and free it with delete in the Foo destructor. You will also have to add a copy constructor and a copy assignment operator to Foo as well, to avoid leaking memory:

    Test.cpp

    Foo::Foo()
    {
        myBar = new Bar;
        myBar->value = 7;
    }
    
    Foo::Foo(const Foo &src)
    {
        myBar = new Bar;
        *myBar = *(src.myBar);
    }
    
    Foo::~Foo()
    {
        delete myBar;
    }
    
    Foo& Foo::operator=(const Foo &rhs)
    {
        *myBar = *(rhs.myBar);
        return *this;
    }
    

    Test.h

    class Foo
    {
    private:
        struct Bar;
        Bar* myBar;
    public:
        Foo();
        Foo(const Foo &src);
        ~Foo();
    
        void printValue();
    
        Foo& operator=(const Foo &rhs);
    };
    

    If you were not implementing PIMPL, then there would be another option. Make myBar be a non-pointer member of the Foo class instead:

    Test.cpp

    Foo::Foo()
    {
        myBar.value = 7;
    }
    
    void Foo::printValue()
    {
        std::cout << "Value = " << myBar.value << std::endl;
    }
    

    Test.h

    class Foo
    {
    private:
        struct Bar
        {
            int value;
        };
    
        Bar myBar;
    
    public:
        Foo();
        void printValue();
    };