2009-04-22 36 views
19

クラスとそのパブリックメソッドを宣言しているが、そのクラスのプライベートメンバーを定義していないC++ヘッダーファイル(.h)を作成することはできますか? few pagesは、クラスとそのすべてのメンバーをヘッダーファイルに宣言し、次にcppファイルでメソッドを個別に定義する必要があると判断しました。私は、Win32 DLLで定義されたクラスを持っていて、それを適切にカプセル化したいので頼みます:そのクラスの内部実装はメンバーを含めて変更されるかもしれませんが、これらの変更はクラスを使用するコード。C++クラスとメソッドを宣言し、メンバーは宣言していないヘッダーファイル?

私がこれを持っていたら、コンパイラは自分のオブジェクトのサイズを事前に知ることができなくなると思います。しかし、コンパイラがコンストラクタを使い、オブジェクトが格納されているメモリ内の場所へのポインタを渡して、 "sizeof(MyClass)"を実行させないようにすれば、それは問題ありません。

更新日:お返事いただきありがとうございます。私が話していたことを達成するには、ピンプルイディオムが良い方法だと思われます。

私のWin32のDLLファイルは、このような個別の機能の束を持っています:私は似た何かをするつもりです

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

これは私があると思いますので、Microsoftが彼らのDLLファイルを書き込み、一般的な方法ですおそらくそれのための正当な理由。

しかし、私はクラスのためのC++の優れた構文を利用したいので、これらの関数をすべてラップするラッパークラスを作成します。それは1人のメンバーを持つでしょう、それは "void * pimpl"になります。このラッパークラスは非常にシンプルなので、私はそれを宣言し、ヘッダーファイルで定義することもできます。しかし、このラッパークラスは本当に私が知る限り、C++コードをきれいに見せる以外の目的はありません。

答えて

33

私は、あなたが探していることは「PIMPLイディオム」と呼ばれるものだと思います。これがどのように動作するかを理解するためには、C++では、そういうものを宣言することができるということを理解する必要があります。

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 
}; 

したがって、forward宣言は、後で型を完全に宣言することを約束することを意味します。その言葉は、 "これはCWidgetと呼ばれることでしょう、私は約束します。

前方宣言のルールは、あなたがポインタまたは前方宣言された何かへの参照を定義することができると言います。これはポインタと参照は実際には単なるアドレスであり、これはまだ定義されていないものです。完全に宣言せずに何かへのポインタを宣言できることは、多くの理由で便利です。

そのあなたが「PIMPL」メソッドを使ってクラスの内部の一部を非表示にするこれを使用することができますので、ここに便利。 Pimplは "実装へのポインタ"を意味します。だから、 "ウィジェット"の代わりに実際の実装であるクラスがあります。ヘッダーで宣言しているクラスは、CImplクラスのパススルーに過ぎません。しくみはこうだ:

// 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(); 
} 

多田:

// 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はパススルーをIMPLにと定義CThingのメソッドを持っています!あなたはあなたのcppにすべての細部を隠してしまっていて、あなたのヘッダーファイルは非常にきれいな方法のリストです。それは素晴らしいことです。上のテンプレートとは違うかもしれないのは、人々がboost :: shared_ptr <>やその他のスマートポインタをimplに使うことができることだけです。それ自体を削除するもの。

また、この方法は、いくつかの厄介な問題が付属しています注意してください。デバッグはちょっと厄介なことがあります(ステップスルーへのリダイレクションの余分なレベル)。また、クラスを作成するためのオーバーヘッドがたくさんあります。すべてのクラスでこれを行うと、すべての入力に飽きてしまうでしょう:)。

3

Google「ピンプルイディオム」または「ハンドルC++」。

+0

より良いスペルの「ピンプル」(「プライベート実装」の場合)? –

3

はい、これは望ましいことです。 1つの簡単な方法は、実装クラスをヘッダーで定義されたクラスから派生させることです。

コンパイラはクラスの作成方法を知らないので、クラスのインスタンスを取得するためのファクトリメソッドが必要です。スタック上にローカルインスタンスを持つことは不可能です。

13

pimplイディオムを使用してください。

-1

+1

リンクが壊れています。 –

0

クラスを宣言するC++ヘッダー ファイル(.H)、および そのパブリックメソッドを作成することが可能ですが、 がプライベートを宣言していないC++でクラスThe Handle-Bodyイディオムをチェックそのメンバーは クラスですか?

最も近い答えはPIMPLイディオムです。

Herb SutterのThe Fast Pimpl Idiomを参照してください。

IMO Pimplは、ヘッダーファイルが何度も変更される開発の初期段階で本当に役に立ちます。 Pimplは、ヒープ上での内部オブジェクトの割り当てや割り当て解除のためにコストがかかります。

7

pimpl idiomは、あなたのクラスにvoid *プライベートデータメンバを追加します。これは素早いものが必要な場合に便利なテクニックです。&しかしそれには欠点があります。その中でも、抽象型に多型を使用することが難しくなっています。時には、抽象基本クラスとその基本クラスのサブクラスが必要な場合があり、ベクトル内のすべての異なる型へのポインタを収集し、それらのメソッドを呼び出します。 PIMPLイディオムの目的は、クラスの実装の詳細を非表示にする場合に加えて、それが唯一ほぼ成功:ポインタ自体は、実装の詳細です。おそらく不透明な実装の詳細です。それにもかかわらず、実装の詳細。

PIMPLイディオムに代わるものは、必要に応じて、多形を使用することができる基本型を提供しながら、インタフェースから実装の詳細の全てを除去するために使用することができる存在します。あなたのDLLのヘッダファイル(クライアントコードから#include 1)で

は、クラスがインスタンス化する方法を指示パブリックメソッドのみと概念(例えば、公共のファクトリメソッド&クローン法)で抽象クラスを作成します。

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ですので、インスタンス化することはできません。プライベートctorを宣言する必要はありません。仮想dtorの存在により、誰かdeleteAnimal*であれば、適切なサブクラスdtorも呼び出されます。

基本タイプの異なるサブクラス(例:dog & cat)を実装するには、DLLに実装レベルのクラスを宣言します。これらのクラスは最終的にヘッダファイルで宣言した抽象基本クラスから派生し、ファクトリメソッドは実際にこれらのサブクラスの1つをインスタンス化します。

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; 
} 
+2

1つのニト - pimplポインタをvoid *にする必要はありません。パブリックヘッダがpimplクラスを参照するためのものです。 –

2

あなたはコンパイラがそうでオブジェクトであり、どのように大規模な知っているように、ヘッダー内のすべてのメンバを宣言する必要があります。

しかし、あなたは、インターフェイスを使用することによってこの問題を解決することができます

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) 
    { 
    ... 
    } 
関連する問題