views:

2527

answers:

8

Is it possible to make a C++ header file (.h) that declares a class, and its public methods, but does not define the private members in that class? I found a few pages that say you should declare the class and all its members in the header file, then define the methods separately in you cpp file. I ask because I want to have a class that is defined in a Win32 DLL, and I want it to be properly encapsulated: the internal implementation of that class might change, including its members, but these changes should not affect code that uses the class.

I guess that if I had this, then it would make it impossible for the compiler to know the size of my objects ahead of time. But that should be fine, as long as the compiler is smart enough to use the constructor and just pass around pointers to the location in memory where my object is stored, and never let me run "sizeof(MyClass)".

Update: Thanks to everyone who answered! It seems like the pimpl idiom is a good way to achieve what I was talking about. I'm going to do something similar:

My Win32 DLL file will have a bunch of separate functions like this:

void * __stdcall DogCreate();
int __stdcall DogGetWeight(void * this);
void __stdcall DogSetWeight(void * this, int weight);

This is the typical way the Microsoft writes their DLL files so I think there is probably good reason for it.

But I want to take advantage of the nice syntax C++ has for classes, so I'll write a wrapper class to wrap up all of these functions. It will have one member, which will be "void * pimpl". This wrapper class will be so simple that I might as well just declare it AND define it in the header file. But this wrapper class really has no purposes other than making the C++ code look pretty as far as I can tell.

+3  A: 

Google "pimple idiom" or "handle C++".

BubbaT
Better spelled 'pimpl' (for 'private implementation')?
Jonathan Leffler
+2  A: 

Yes, this can be a desireable thing to do. One easy way is to make the implementation class derive from the class defined in the header.

The downside is that the compiler won't know how to construct your class, so you'll need some kind of factory method to get instances of the class. It will be impossible to have local instances on the stack.

Mark Ransom
+10  A: 

Use pimpl idiom.

bb
+1  A: 

Check out the class The Handle-Body Idiom in C++

J.W.
A: 

Is it possible to make a C++ header file (.h) that declares a class, and its public methods, but does not declare the private members in that class?

The most nearest answer is PIMPL idiom.

Refer this The Fast Pimpl Idiom from Herb Sutter.

IMO Pimpl is really useful during initial stages of development where your header file is going to change many times. Pimpl has its cost due to its allocation\deallocation of internal object on heap.

aJ
+6  A: 

I think what you are looking for is something called the "pimpl idiom". To understand how this works, you need to understand that in C++ you can forward declare something like so.

class CWidget; // Widget will exist sometime in the future
CWidget* aWidget;  // An address (integer) to something that 
                   // isn't defined *yet*

// later on define CWidget to be something concrete
class CWidget
{
     // methods and such 
};

So to forward declare means to promise to fully declare a type later. Its saying "there will be this thing called a CWidget, I promise. I'll tell you more about it later.".

The rules of forward declaration say that you can define a pointer or a reference to something that has been forward declared. This is because pointers and references are really just addresses-a number where this yet-to-be-defined thing will be. Being able to declare a pointer to something without fully declaring it is convenient for a lot of reasons.

Its useful here because you can use this to hide some of the internals of a class using the "pimpl" method. Pimpl means "pointer to implementation". So instead of "widget" you have a class that is the actual implementation. The class you are declaring in your header is just a pass-through to the CImpl class. Here's how it works:

// Thing.h

class CThing
{
public:
    // CThings methods and constructors...
    CThing();
    void DoSomething();
    int GetSomething();
    ~CThing();
private:
    // CThing store's a pointer to some implementation class to 
    // be defined later
    class CImpl;      // forward declaration to CImpl
    CImpl* m_pimpl;  // pointer to my implementation
};

Thing.cpp has CThing's methods defined as pass-throughs to the impl:

// Fully define Impl
class CThing::CImpl
{
private:
     // all  variables
public:
     // methods inlined
     CImpl()
     {
          // constructor
     }

     void DoSomething()
     {
          // actual code that does something
     }
     //etc for all methods     
};

// CThing methods are just pass-throughs
CThing::CThing() : m_pimpl(new CThing::CImpl());
{
}  

CThing::~CThing()
{
    delete m_pimpl;
}

int CThing::GetSomething()
{
    return m_pimpl->GetSomething();
}

void CThing::DoSomething()
{
    m_impl->DoSomething();
}

tada! You've hidden all the details in your cpp and your header file is a very tidy list of methods. Its a great thing. The only thing you might see different from the template above is that people may use boost::shared_ptr<> or other smart pointer for the impl. Something that deletes itself.

Also, keep in mind this method comes with some annoyances. Debugging can be a tad bit annoying (extra level of redirection to step through). Its also a lot of overhead for creating a class. If you do this for every class, you'll get tired of all the typing :).

Doug T.
+3  A: 

The pimpl idiom adds a void* private data member to your class, and this is a useful technique if you need something quick & dirty. It has its drawbacks however. Main among those is it makes it difficult to use polymorphism on the abstract type. Sometimes you might want an abstract base class and subclasses of that base class, collect pointers to all the different types in a vector and call methods on them. In addition, if the purpose of the pimpl idiom is to hide the implementation details of the class then it only almost succeeds: the pointer itself is an implementation detail. An opaque implementation detail, perhaps. But an implementation detail nonetheless.

An alternative to the pimpl idiom exists which can be used to remove all of the implementation details from the interface while providing a base type that can be used polymorphically, if needed.

In your DLL's header file (the one #included by client code) create an abstract class with only public methods and concepts which dictate how the class is to be instantiated (eg, public factory methods & clone methods):

kennel.h

/****************************************************************
 ***
 ***    The declaration of the kennel namespace & its members
 ***    would typically be in a header file.
 ***/

// Provide an abstract interface class which clients will have pointers to.
// Do not permit client code to instantiate this class directly.

namespace kennel
{
    class Animal
    {
    public:
     // factory method
     static Animal* createDog(); // factory method
     static Animal* createCat(); // factory method

     virtual Animal* clone() const = 0; // creates a duplicate object
     virtual string speak() const = 0; // says something this animal might say
     virtual unsigned long serialNumber() const = 0; // returns a bit of state data
     virtual string name() const = 0; // retuyrns this animal's name
     virtual string type() const = 0; // returns the type of animal this is

     virtual ~Animal() {}; // ensures the correct subclass' dtor is called when deleteing an Animal*
    };
};

...Animal is an abstract base class and so cannot be instantiated; no private ctor needs to be declared. The presence of the virtual dtor ensures that if someone deletes an Animal*, the proper subclass' dtor will also be called.

In order to implement different subclasses of the base type (eg dogs & cats), you would declare implementation-level classes in your DLL. These classes derive ultimately from the abstract base class you declared in your header file, and the factory methods would actually instantiate one of these subclasses.

dll.cpp:

/****************************************************************
 ***
 ***    The code that follows implements the interface
 ***    declared above, and would typically be in a cc
 ***    file.
 ***/   

// Implementation of the Animal abstract interface
// this implementation includes several features 
// found in real code:
//   Each animal type has it's own properties/behavior (speak)
//   Each instance has it's own member data (name)
//   All Animals share some common properties/data (serial number)
//

namespace
{
    // AnimalImpl provides properties & data that are shared by
    // all Animals (serial number, clone)
    class AnimalImpl : public kennel::Animal 
    {
    public:
     unsigned long serialNumber() const;
     string type() const;

    protected:
     AnimalImpl();
     AnimalImpl(const AnimalImpl& rhs);
     virtual ~AnimalImpl();
    private:
     unsigned long serial_;    // each Animal has its own serial number
     static unsigned long lastSerial_; // this increments every time an AnimalImpl is created
    };

    class Dog : public AnimalImpl
    {
    public:
     kennel::Animal* clone() const { Dog* copy = new Dog(*this); return copy;}
     std::string speak() const { return "Woof!"; }
     std::string name() const { return name_; }

     Dog(const char* name) : name_(name) {};
     virtual ~Dog() { cout << type() << " #" << serialNumber() << " is napping..." << endl; }
    protected:
     Dog(const Dog& rhs) : AnimalImpl(rhs), name_(rhs.name_) {};

    private:
     std::string name_;
    };

    class Cat : public AnimalImpl
    {
    public:
     kennel::Animal* clone() const { Cat* copy = new Cat(*this); return copy;}
     std::string speak() const { return "Meow!"; }
     std::string name() const { return name_; }

     Cat(const char* name) : name_(name) {};
     virtual ~Cat() { cout << type() << " #" << serialNumber() << " escaped!" << endl; }
    protected:
     Cat(const Cat& rhs) : AnimalImpl(rhs), name_(rhs.name_) {};

    private:
     std::string name_;
    };
};

unsigned long AnimalImpl::lastSerial_ = 0;


// Implementation of interface-level functions
//  In this case, just the factory functions.
kennel::Animal* kennel::Animal::createDog()
{
    static const char* name [] = {"Kita", "Duffy", "Fido", "Bowser", "Spot", "Snoopy", "Smkoky"};
    static const size_t numNames = sizeof(name)/sizeof(name[0]);

    size_t ix = rand()/(RAND_MAX/numNames);

    Dog* ret = new Dog(name[ix]);
    return ret;
}

kennel::Animal* kennel::Animal::createCat()
{
    static const char* name [] = {"Murpyhy", "Jasmine", "Spike", "Heathcliff", "Jerry", "Garfield"};
    static const size_t numNames = sizeof(name)/sizeof(name[0]);

    size_t ix = rand()/(RAND_MAX/numNames);

    Cat* ret = new Cat(name[ix]);
    return ret;
}


// Implementation of base implementation class
AnimalImpl::AnimalImpl() 
: serial_(++lastSerial_) 
{
};

AnimalImpl::AnimalImpl(const AnimalImpl& rhs) 
: serial_(rhs.serial_) 
{
};

AnimalImpl::~AnimalImpl() 
{
};

unsigned long AnimalImpl::serialNumber() const 
{ 
    return serial_; 
}

string AnimalImpl::type() const
{
    if( dynamic_cast<const Dog*>(this) )
     return "Dog";
    if( dynamic_cast<const Cat*>(this) )
     return "Cat";
    else
     return "Alien";
}

Now you have the interface defined in the header & the implementation details completely seperated out where client code can't see it at all. You would use this by calling methods declared in your header file from code that links to your DLL. Here's a sample driver:

main.cpp:

std::string dump(const kennel::Animal* animal)
{
    stringstream ss;
    ss << animal->type() << " #" << animal->serialNumber() << " says '" << animal->speak() << "'" << endl;
    return ss.str();
}

template<class T> void del_ptr(T* p)
{
    delete p;
}

int main()
{
    srand((unsigned) time(0));

    // start up a new farm
    typedef vector<kennel::Animal*> Animals;
    Animals farm;

    // add 20 animals to the farm
    for( size_t n = 0; n < 20; ++n )
    {
     bool makeDog = rand()/(RAND_MAX/2) != 0;
     if( makeDog )
      farm.push_back(kennel::Animal::createDog());
     else
      farm.push_back(kennel::Animal::createCat());
    }

    // list all the animals in the farm to the console
    transform(farm.begin(), farm.end(), ostream_iterator<string>(cout, ""), dump);

    // deallocate all the animals in the farm
    for_each( farm.begin(), farm.end(), del_ptr<kennel::Animal>);

    return 0;
}
John Dibling
One nit - there's no need for the pimpl pointer to be a void*. All that needs to be done is for the public header to forward reference the pimpl class.
Michael Burr
A: 

You have to declare all members in the header so the compiler knows how large is the object and so on.

But you can solve this by using an interface:

ext.h:

class ExtClass
{
public:
  virtual void func1(int xy) = 0;
  virtual int func2(XYClass &param) = 0;
};

int.h:

class ExtClassImpl : public ExtClass
{
public:
  void func1(int xy);
  int func2(XYClass&param);
};

int.cpp:

  void ExtClassImpl::func1(int xy)
  {
    ...
  }
  int ExtClassImpl::func2(XYClass&param)
  {
    ...
  }
rstevens