2017-02-12 9 views
0

次のヘッダーファイルと実装ファイルには、すべての3つの概念を組み合わせたが、コンパイルされません。Pimplイディオム、個別のインターフェイス/実装ファイル、および複数の仮想継承。どうやって?

$ cat a.h 
#include <memory> 

class Base { 
protected: 
    class BaseImpl; 
    std::shared_ptr<BaseImpl> pImpl; 
    Base(BaseImpl* impl) 
     : pImpl{impl} 
    {} 
public: 
    virtual ~Base() 
    {} 
    virtual void func() =0; 
}; 

class Der : public virtual Base { 
private: 
    class DerImpl; 
    DerImpl* getPimpl() const noexcept; 
public: 
    Der(); 
    virtual ~Der() 
    {} 
    void func(); 
}; 
$ cat a.cpp 
#include "a.h" 

class Base::BaseImpl { 
}; 

class Der::DerImpl : public virtual Base::BaseImpl { 
public: 
    void func() {} 
}; 

Der::Der() 
    : Base{new DerImpl()} 
{} 

Der::DerImpl* Der::getPimpl() const noexcept { 
    return static_cast<DerImpl*>(pImpl.get()); 
} 

void Der::func() { 
    getPimpl()->func(); 
} 
$ g++ --std=c++11 -c a.cpp 
a.cpp: In member function ‘Der::DerImpl* Der::getPimpl() const’: 
a.cpp:16:45: error: cannot convert from base ‘Base::BaseImpl’ to derived type ‘Der::DerImpl’ via virtual base ‘Base::BaseImpl’ 
    return static_cast<DerImpl*>(pImpl.get()); 
              ^

あなたはなぜ、間違っているものを私に教えてくださいだろう、と私はPIMPLイディオムで別の宣言と定義ファイルを持っているかもしれない方法複数の仮想継承。

+0

http://stackoverflow.com/questions/7484913/why-cant-static-cast-be-used-to-down-cast-仮想継承が関与するとき –

+0

@MMそれだけです。ありがとう。 –

答えて

0

複数の仮想基本クラスquestionとpimplイディオムは全く異なるトピックです。 pimplとimplは通常、別々のヘッダーとcppファイルにあります。 pimplのイディオム全体は実装へのポインタです。つまり、ピンプルはimplにそれらをクライアントに公開する関数をラップし、pimplのcppファイルの実際のimplにキャストされたimplへのvoidポインタのみを含み、これを作成して破棄しますそれはコンストラクタとデコンストラクタです。必要に応じてさらに多くの機能を実装することもできますが、このタイプのイディオムは、通常、バイナリdllとともに出荷されるヘッダファイルに知的財産が公開されないようにするために使用されます。また、製品の将来のリリースでクライアントアプリケーションを誤って中断しないように、インターフェイスを固めておくこともできます。

ライブラリ共有用であり、単一アプリケーションの生産コードではありません。基底クラスと継承に関しては、基底クラスには自己のインスタンスへのポインタがあります。一度に一つに集中できます。 Thisは、pimplイディオムを詳しく説明するのに役立ちます。あなたの製品に複数の仮想継承が必要な場合は、必要に応じてpimplイディオムを使用してライブラリを公開するだけです。複数の継承をリフレッシュする必要がある場合は、hereがあります。

0

static_castから派生型から基本型にコンパイルエラーが伝えられません。 dynamic_castまたはreinterpret_castのいずれかを使用します(reinterpret_castは高速ですが、複数継承で正常に動作するとは限りません)。

p.s.また、仮想基本クラスの仮想デストラクタも忘れないでください。

+0

'dynamic_cast'は動作せず、' reinterpret_cast'は意図された多重継承環境(SIGSEGVを引き起こしました)ではオプションではありません。 –

+0

@SteveEmmersonはRTTIを利用していますか?そうであれば、 'dynamic_cast'が動作するはずです。 –

+0

私は分かりません。しかし、 'dynamic_cast'を使うよりも効率的な回避策が見つかったので、その点は疑問です。 –

0

私がベースで、ポインタの変更を避けるために、実装にpImplvoidへのポインタではなく、ポインタを保存

  • によって概念(個別のファイルや複数の仮想継承とPIMPL)を組み合わせることに成功しコンストラクタ;
  • static_castではなく、getPimpl()を派生クラスで使用すると、仮想継承環境でのダウンキャストができなくなる可能性があります。

は、ここでは、コードとテストルーチンです:

$ cat a.h 
#include <memory> 

class Base { 
protected: 
    class Impl; 
    std::shared_ptr<void> pImpl; 
    Base(void* impl) : pImpl{impl} {} 
public: 
    virtual ~Base() =0; 
}; 

class Der1 : public virtual Base { 
protected: 
    class Impl; // Won't compile if `private` 
private: 
    Impl* getPimpl() const noexcept; 
public: 
    Der1(); 
    virtual ~Der1() {} 
    void der1Func(); 
}; 

class Der2 : public virtual Base { 
protected: 
    class Impl; // Won't compile if `private` 
private: 
    Impl* getPimpl() const noexcept; 
public: 
    Der2(); 
    virtual ~Der2() {} 
    void der2Func(); 
}; 

class Joined : public Der1, public Der2 { 
private: 
    class Impl; 
    Impl* getPimpl() const noexcept; 
public: 
    Joined(); 
    void joinedFunc(); 
}; 
$ cat a.cpp 
#include "a.h" 
#include <iostream> 

class Base::Impl {}; 

class Der1::Impl : public virtual Base::Impl { 
public: 
    void der1Func() { 
     std::cout << "Der1::Impl::der1Func() called\n"; 
    } 
}; 

class Der2::Impl : public virtual Base::Impl { 
public: 
    void der2Func() { 
     std::cout << "Der2::Impl::der2Func() called\n"; 
    } 
}; 

class Joined::Impl : public virtual Der1::Impl, public virtual Der2::Impl { 
public: 
    void joinedFunc() { 
     std::cout << "Joined::Impl::joinedFunc() called\n"; 
    } 
}; 

Base::~Base() { 
    reinterpret_cast<Impl*>(pImpl.get())->~Impl(); 
} 

Der1::Der1() : Base{new Impl()} {} 

Der1::Impl* Der1::getPimpl() const noexcept { 
    return reinterpret_cast<Impl*>(pImpl.get()); 
} 

void Der1::der1Func() { 
    getPimpl()->der1Func(); 
} 

Der2::Der2() : Base{new Impl()} {} 

Der2::Impl* Der2::getPimpl() const noexcept { 
    return reinterpret_cast<Impl*>(pImpl.get()); 
} 

void Der2::der2Func() { 
    getPimpl()->der2Func(); 
} 

Joined::Joined() : Base{new Impl()} {} 

Joined::Impl* Joined::getPimpl() const noexcept { 
    return reinterpret_cast<Impl*>(pImpl.get()); 
} 

void Joined::joinedFunc() { 
    getPimpl()->joinedFunc(); 
} 

int main() { 
    Joined* joined = new Joined(); 
    joined->joinedFunc(); // Calls Der1::joinedFunc() 
    joined->der1Func(); // Calls Der1::der1Func() 
    joined->der2Func(); // Calls Der2::der2Func() 
    Der1* der1 = joined; 
    der1->der1Func();  // Calls Der1::der1Func() 
} 
$ g++ --std=c++11 a.cpp && ./a.out 
Joined::Impl::joinedFunc() called 
Der1::Impl::der1Func() called 
Der2::Impl::der2Func() called 
Der1::Impl::der1Func() called 
$