Question

I've been trying to have a go at a 'pimpl' idiom but I just can't get the darned thing to compile.

On Linux Mint with g++ v. 4.6.3 I get the following error:

$ g++ main.cc 
/tmp/ccXQ9X9O.o: In function `main':
main.cc:(.text+0xd7): undefined reference to `Person::Person(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
collect2: ld returned 1 exit status

This is my code:

person.hh

#ifndef PERSON_HH
#define PERSON_HH

#include <tr1/memory>
#include <string>

class Person
{
    private:
    class PersonImpl;
    std::tr1::shared_ptr<PersonImpl> pImpl;

    public:
    Person(const std::string& name, int age=0);

    ~Person();

    const std::string& get_name() const;

    int get_age() const;
};

#endif

person.cc

#include <string>
#include "person.hh"

class Person::PersonImpl
{
    public:
    std::string name;
    int age;

    PersonImpl(const std::string& n, int a) : name(n), age(a) {}
};

Person::Person(const std::string& name, int age) : pImpl(new PersonImpl(name, age)) {}

Person::~Person() {}

const std::string& Person::get_name() const { return pImpl->name; }

int Person::get_age() const { return pImpl->age; }

main.cc

#include <iostream>
#include "person.hh"

int main()
{   
    const std::string name = "foo";
    Person p(name, 50);

    return 0;
}

Apart from the code mistakes, could you please advise on the approach I've taken to mimicking a 'pimpl' idiom? Does this conform to it?

Was it helpful?

Solution

The problem seems to be due to the fact that your person.cc file is not being linked in. You may have to adjust your project configuration to fix this.

Apart from the code mistakes, could you please advise on the approach I've taken to mimicking a 'pimpl' idiom? Does this conform to it?

I would suggest using unique_ptr rather than shared_ptr, since the PersonImpl implementation object is owned exclusively by the Person object:

class Person
{
private:
    class PersonImpl;
    std::tr1::unique_ptr<PersonImpl> pImpl;
//            ^^^^^^^^^^
    // ...
};

Apart from this, you should make use of constructor initialization lists to initalize the pImpl data member:

Person::Person(const std::string& name, int age)
    : pImpl(new PersonImpl(name, age))
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

OTHER TIPS

You need to build with both source files. This can either be done be just putting both source file on the command line:

$ g++ -Wall -g main.cc person.cc

Or by compiling them one by one to object files, and then linking them together

$ g++ -Wall -g main.cc -c
$ g++ -Wall -g person.cc -c
$ g++ main.o person.o

The -c option tells GCC to create an object file instead of trying to link. -Wall enables more warnings, which are always a good idea (they can indicate some unintended behavior), and -g tells GCC to generate debug information (good while debugging, especially if a debugger is needed as the debug information includes symbol names).

You're getting a linker error, not a compilation error. When linking, you must list all of your program's source files:

g++ main.cc person.cc

Or, to compile only, use -c:

g++ -c main.cc

EDIT

Additionally, you Person constructor is wrong. You're treating pImpl as a function, where I assume you want to initalise it. You need to use the mem-initialiser-list syntax for that:

Person::Person(const std::string& name, int age)
    : pImpl(new PersonImpl(name, age));
{}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top