pimpl idiom增加了一個void *私有數據成員給你的類,如果你需要快速的&髒東西,這是一個有用的技巧。但是它有缺點。其中主要是它使抽象類型上使用多態性變得困難。有時你可能需要一個抽象基類和該基類的子類,收集指向矢量中所有不同類型的指針並調用它們的方法。另外,如果pimpl習慣用法的目的是隱藏類的實現細節,那麼它只有差不多成功:指針本身是一個實現細節。也許是一個不透明的實現細節。但是,一個實現細節。
存在一種替代pimpl習語的方法,可用於從接口中刪除所有實現細節,同時提供可根據需要多形地使用的基本類型。
在您的DLL的頭文件(一個由客戶端代碼執行#included)創建一個唯一的公共方法和概念的抽象類,它決定了類是如何被實例化(例如,公共工廠方法&克隆方法):
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*
};
};
...動物是abstract base class,因此不能被實例化;沒有私營公司需要申報。虛擬dtor的存在確保瞭如果某人delete
是Animal*
,那麼也會調用適當的子類「dtor」。
爲了實現基類型的不同子類(例如,狗&貓),您需要在DLL中聲明實現級類。這些類最終來自您在頭文件中聲明的抽象基類,而工廠方法實際上會實例化這些子類中的一個。
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";
}
現在,你必須在頭&定義的接口實現的細節完全分隔出來,其中客戶端代碼無法看到它。您可以通過調用鏈接到您的DLL的代碼在頭文件中聲明的方法來使用它。下面是一個示例驅動程序:
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;
}
拼寫更好的'pimpl'(用於'私人執行')? – 2009-04-22 19:41:32