2009-10-25 18 views
7

C#のクラス拡張メソッドのような元のクラス定義(クラスと対応する.hファイルを含むコンパイル済み.lib)を変更せずに、クラスに新しいメソッドを追加する方法はありますか?C++クラス拡張

+1

今後の参考として、これは「Monkey patching」と呼ばれています。 – LiraNuna

答えて

15

No. C++にはこのような機能はありません。

他の回答で述べたように、一般的な回避策は次のとおり

  • おそらくdecoratorクラス
  • が動作機能を定義する定義
  • 実際の実装クラスを非表示にする工場で、派生クラスを定義クラスのインスタンスの場合
+0

[ダブルディスパッチ](http://ja.wikipedia.org/wiki/Double_dispatch) – Bossliaw

11

いいえ、これはC++では実行できません。

あなたはこのような何かを達成したいならば、あなたは2つのオプションを持っている

    あなたは、このオプションがある場合は、クラスが書き込まれていないかもしれないとして、それは法的ではないかもしれません(クラスから継承することができ
  • 同じインターフェイス+新しいメソッドを持ち、拡張したいラッパークラスに独自のラッパークラスを作成することができます。

私は委任アプローチを推奨します。

+4

+1でも「相続財産よりも優先」 – Rob

+0

良い返信 - しかし、私は誰かが質問をするようにこれらは、 "継承"や "代理"のような用語を聞くときに何を実装するかを知っています。 –

+1

@mh、おそらくそうではありません。しかし今、彼らはSOやGoogleの検索に入れるべき正しい言葉を知っている – Glen

-2

申し訳ありませんが、コードがobjに入ったら、それを変更することはできません。これをVCで行うことができれば、部分クラスは既にサポートされています。しかし、1つの例外があります。演算子メソッドは、グローバル関数を使って拡張できます.cout < <がSTLでどのように実装されているかとよく似ています。

5

C#クラスの拡張メソッドは、ほとんどが構文的な砂糖です。自由な関数(つまり、最初のパラメーターとしてクラスへの参照または定数参照を持つ関数)を使用して同じ機能を得ることができます。これはSTLでもうまくいくので、あなたのクラスではいかがですか?

-3

あなたができることを確認:それはしかし、最良の方法だというわけではありません


template <typename Ext> 
class Class: public Ext { /* ... */ }; 

1

一般的にはありません。ただし、ライブラリで拡張機能が必要なクラスのインスタンスが作成されず、クラスのインスタンスを作成して拡張機能を必要とするアプリケーション内のすべての場所を変更できる場合は、次のような方法があります。

  • クラスのインスタンスを必要とするすべての場所で呼び出され、インスタンスへのポインタを返すファクトリ関数を作成します(のデザインパターンファクトリ、...)。
  • 必要な拡張子を持つ派生クラスを作成します。
  • ファクトリ関数が、元のクラスではなく、派生クラスを返すようにします。

例:あなたは拡張子にアクセスする必要があるときはいつでも


    class derivedClass: public originalClass { /* ... */}; 

    originalClass* createOriginalClassInstance() 
    { 
     return new derivedClass(); 
    } 
  • 、あなたはもちろんの派生クラスにオリジナルキャストを、キャストする必要があります。

これは、グレンが提案する「継承」メソッドの実装方法です。 Glenの "同じインターフェースを持つラッパークラス"メソッドも理論的には非常にいいですが、あなたのケースではうまく動作しないようにプロパティが少し異なります。

1

これには1つの方法があります。それはあなたの要件を少し緩和することです。 C++では、クラスのインターフェイスはメンバー関数だけでなく、で動作するすべての関数で構成されています。

つまり、パラメータとしてクラスを与えることができる非メンバ関数は、そのインタフェースの一部と考えるべきです。

たとえば、std::find()またはstd::sort()は、クラスのメンバーではないにもかかわらず、std::vectorのインターフェイスの一部です。

この定義を受け入れると、非メンバ関数を追加するだけでクラスを拡張できます。

0

バイナリ形式のクラスファイルにメソッドやデータを物理的に追加することはできません。ただし、拡張クラスを作成することによって、そのクラスのオブジェクトにメソッドとデータ(機能と状態)を追加できます。これは単純ではなく、Meta-Object-ProtocolとInterfaceに基づくプログラミングが必要です。あなたはC++でこれを達成するために多くのことをする必要があります。なぜなら、これは、そのままの状態でのReflectionをサポートしていないからです。このような実装では、元のクラスオブジェクトポインタを介して新しい拡張クラスによって実装されたインタフェースをクエリすると、メタオブジェクト実装は実行時に作成する拡張クラスのメタクラスオブジェクトを介してそのインタフェースポインタを返します。 これはカスタマイズ可能な(プラグインベースの)ソフトウェアアプリケーションフレームワークの数です。ただし、オブジェクト関係が記述されている辞書を使用してすべてのクラスのメタオブジェクトをインスタンス化し、元のクラスオブジェクトと拡張クラスオブジェクトの正しいインタフェースポインタを与えるためには、他の多くのMOPメカニズムが必要です。ダッソー・システムズのCATIA V5は、CAA V5というアーキテクチャーで書かれており、既存のコンポーネントを拡張することができます。

3

C++ではフリー関数を使用できますが、多くの関数を一緒にネストすると拡張メソッドがうまく機能することがあります。このC#のコードを見てみましょう:

var r = numbers.Where(x => x > 2).Select(x => x * x); 

我々はそれが次のようになりフリー機能を使用してC++でこれを書くためにした場合:

auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; }); 

だけでなく、これは読みにくいですが、それは難しいです書く。これを解決する一般的な方法は、パイプライン機能と呼ばれるものを作成することです。これらの関数は、|パイプ演算子(実際には演算子)をオーバーロードすることによって作成されます。したがって、上のコードは次のように書くことができます:

auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; }); 

読み書きがはるかに簡単です。多くのライブラリは、範囲に対してパイプ機能を使用しますが、他のクラスにも拡張できます。 Boostはrangeライブラリでそれを使用し、pstade ovenはそれを使用し、このC++ linqライブラリもこのライブラリを使用します。

独自の配管機能を記述したい場合は、方法を説明してください。hereしかし、他のライブラリーは、機能アダプターを提供しています。 Pstadeの卵はpipable adaptor、linqは少なくとものアダプタを提供して、範囲のパイプライン機能を最小限にします。

struct contains_t 
{ 
    template<class Range, class T> 
    bool operator()(Range && r, T && x) const 
    { return (r | linq::find(x)) != boost::end(r); }; 
}; 

次に、あなたはこのように静的初期化を使用して機能を初期化します:

range_extension<contains_t> contains = {}; 

次に、あなたがあなたのを使用することができますLINQ、あなたは最初だけで、このような関数オブジェクトとして、あなたの関数を作成を使用して

if (numbers | contains(5)) printf("We have a 5");