[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Pimpl idiom
From:       Peter_Kümmel <syntheticpp () gmx ! net>
Date:       2006-02-12 20:10:14
Message-ID: 43EF9626.9020509 () gmx ! net
[Download RAW message or body]

Hello,

I've read this in the TODO file of kdelibs:

- Change all FooPrivate *d; -> Private * const d; and place initialization
  in the constructor (for classes that would benefit from this).  To help catch silly
  mistakes since d should never change. Also consider changing to use KStaticDeleter to
  help prevent mistakes where developers forget to delete the pointer. Maybe make use of
  Qt4 helper macros?
  (Frerich)


The problem with such a solution is:
- you can forget to create the private class
- you can forget to delete d
- using a pure pointer for the pimpl violates const correctness.
  It is possible to call non const member functions of the implementation object


Attached a proposal for a different implementation of the Pimpl idiom,
which has not above problems.

It automatically creates on construction, and deletes on destruction the
private implementation object, it also propagates the constness to the
private object.
It is pure C++ and needs no macros or static variables.


Cheers,
Peter


See also:
http://www.gotw.ca/publications/mill04.htm
http://www.gotw.ca/publications/mill05.htm
http://boost-consulting.com/vault/




["Makefile" (text/plain)]

BIN = main
CXXFLAGS = -Wall -Wold-style-cast -Wundef -Wsign-compare -Wconversion -Wpointer-arith -pedantic -O2
CPPFLAGS = -I. -DNDEBUG

.PHONY: build clean
build: $(BIN)
clean:
	rm -f $(BIN) $(BIN).exe $(BIN).o

["main.cpp" (text/plain)]

#include <type.h>
#include <iostream>



/////////////////////////////////////////
// Definition of ImplT<A>
/////////////////////////////////////////

template<>
struct KImplDef<A>
{
    KImplDef() { std::cout << "A created\n"; }
    ~KImplDef(){ std::cout << "A destroyed\n"; }

    void foo() { std::cout << "Impl<A>::foo() \n"; }
    void foo() const { std::cout << "Impl<A>::foo() const \n"; }
};

/////////////////////////////////////////
// class A definition
/////////////////////////////////////////
A::A()
{}

void A::foo(){ d->foo(); }

void A::foo() const { d->foo(); }



/////////////////////////////////////////
// main
/////////////////////////////////////////


int main()
{
    A* a = new A;
    const A* ca = new A;

    a->foo();
    ca->foo();

    delete a;
    delete ca;

    return 0;
}

["KImpl.h" (text/plain)]

#ifndef KPIMPL_H
#define KPIMPL_H

#include <memory>


/////////////////////
// KImpl
/////////////////////

template
<    
    class T, 
    typename Pointer = std::auto_ptr<T>
>
class KPimpl
{
public:

    typedef T Impl;

    KPimpl() : ptr_(new T)
    {}

    T* operator->()
    {
        return ptr_.operator->();
    }

    T& operator*()
    {
        return ptr_.operator*();
    }

    const T* operator->() const
    {
        return ptr_.operator->();
    }

    const T& operator*() const
    {
        return ptr_.operator*();
    }

    Pointer& wrapped()
    {
        return ptr_;
    }

    const Pointer& wrapped() const
    {
        return ptr_;
    }


private:
    KPimpl(const KPimpl&);
    KPimpl& operator=(const KPimpl&);

    Pointer ptr_;
};



//////////////////////////////////////////
//  template for the implementations
//////////////////////////////////////////

template<class T>
struct KImplDef;



template<class T, template<class> class SmartPtr = std::auto_ptr >
struct KImpl
{
    typedef KPimpl
        <
            KImplDef<T>, 
            SmartPtr< KImplDef<T> >
        > 
        Ptr;
};


#endif

["type.h" (text/plain)]

#include <KImpl.h>


/////////////////////////////////////////
// class A declaration
/////////////////////////////////////////

class A
{
public:
    A();
    void foo();
    void foo() const;

private:
    KImpl<A>::Ptr d;
};







[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic