5

実行時にクラスにフィールド (以前は存在しなかったフィールド) を追加する方法はありますか? このスニペットのようなもの:

Myobject *ob; // create an object
ob->addField("newField",44); // we add the field to the class and we assign an initial value to it
printf("%d",ob->newField); // now we can access that field

私はそれがどのように行われるか、それが醜いハックであろうとなかろうと気にしません。

別の例:このクラスを記述した XML ファイルがあるとします。

<class name="MyClass">
   <member name="field1" />
   <member name="field2" />
</class>

フィールド「field1」と「field2」をクラスに「追加」したい(クラスがすでに存在すると仮定)。これがクラスのコードだとしましょう:

class MyClass {
};

実行時にクラスを作成したくありません。メンバー/フィールドを既存のものに追加したいだけです。

ありがとうございました !

4

6 に答える 6

13

マップとバリアントを使用します。

たとえば、boost::variant を使用します。http://www.boost.org/doc/libs/1_36_0/doc/html/variant.htmlを参照してください

(ただし、もちろん、XML 属性のタイプに合わせて独自のものを作成することもできます。)

#include <map>
#include <boost/variant.hpp>

typedef boost::variant< int, std::string > MyValue ;
typedef std::map<std::string, MyValue> MyValueMap ;

MyValueMap をクラスのメンバーとして追加することにより、名前に従ってプロパティを追加できます。つまり、コードは次のとおりです。

oMyValueMap.insert(std::make_pair("newField", 44)) ;
oMyValueMap.insert(std::make_pair("newField2", "Hello World")) ;
std::cout << oMyValueMap["newField"] ;
std::cout << oMyValueMap["newField2"] ;

これを MyObject クラスにカプセル化し、この MyObject クラスにオーバーロードされた適切なアクセサーを追加することで、上記のコードはいくらか明確になります。

oMyObject.addField("newField", 44) ;
oMyObject.addField("newField2", "Hello World") ;
std::cout << oMyObject["newField"] ;
std::cout << oMyObject["newField2"] ;

ただし、そうすると、C++ の型の安全性がいくらか失われます。しかし、XML の場合、これは避けられないことだと思います。

于 2008-10-24T22:40:08.187 に答える
4

コンパイラはコンパイル時に参照を解決する必要があるため、説明した方法でそれを行う方法はありません-エラーが発生します。

ただし、ユニバーサル デザイン パターンを参照してください。

于 2008-10-24T22:04:01.927 に答える
3

その構文を機能させることはできませんが (コンパイル時の静的チェックのため)、構文を変更する意思がある場合は、同じ効果を非常に簡単に実現できます。string->blob マッピングを持つディクショナリ メンバーを作成し、次のようなメンバー関数を作成するのはかなり簡単です。

template< typename T > T get_member( string name );
template< typename T > void set_member( string name, T value );

必要に応じて、構文をよりコンパクト/トリッキーにすることができます (例: '->' 演算子のオーバーライドを使用)。活用できるコンパイラ固有のトリックもいくつかあります (たとえば、MSVC は __declspec(property) をサポートしています。これにより、メンバー変数への参照を特定の形式のメソッドにマップできます)。ただし、結局のところ、コンパイラが言語で受け入れないことを実行してコンパイルすることはできません。

于 2008-10-24T22:31:37.457 に答える
2

短いバージョン:できません。これに対するネイティブ サポートはありません。c++ は静的に型付けされており、コンパイラは操作する各オブジェクトの構造を認識している必要があります。

推奨事項:組み込みインタプリタを使用してください。また、独自のものを作成しないでください (以下を参照)。既に動作し、デバッグされているものを入手してください。


できること:必要に応じて必要なだけインターパーターを実装します。

次のようなデータメンバーを使用してクラスをセットアップするのは簡単です

std::vector<void*> extra_data;

実行時に任意のデータを添付できます。これのコストは、次のような方法でそのデータを手動で管理する必要があることです。

size_t add_data_link(void *p); // points to existing data, returns index
size_t add_data_copy(void *p, size_t s) // copies data (dispose at
                                        // destruction time!), returns 
                                        // index 
void* get_data(size_t i); //...

しかし、それは制限ではありません。もう少し注意すれば、任意のデータを名前に関連付けることができ、このスキームを好きなだけ詳しく説明し続けることができます (型情報を追加するなど...)。 down to は、実行時の柔軟性を確保するためにインターパーターを実装しています。

于 2008-10-24T22:07:57.770 に答える
0

私はこれを見て、少し検索しました。:Michael Hammerのブログから取得したこのコードスニペットは、 boost::any を使用してこれを行うための良い方法のようです。

最初に、キー(つまり変数名)と値を含むstd::mapを定義する構造を定義します。ペアを追加し、値を取得する関数と一緒に設定する関数が定義されています。私に言わせればかなり簡単ですが、もっと複雑なことをする前に始めるのは良い方法のようです。

struct AnyMap {
  void addAnyPair( const std::string& key , boost::any& value );

  template<typename T>
  T& get( const std::string key ) {
    return( boost::any_cast<T&>(map_[key]) );
  }

  std::map<const std::string, boost::any> map_;
};

void AnyMap::addAnyPair( const std::string& key , boost::any& value ) {
  map_.insert( std::make_pair( key, value ) );
}

結論として、これはハックです。C++は厳密な型チェック言語であり、したがって、ルールを曲げる人々の中にモンスターが存在するからです。

于 2011-12-16T16:55:53.260 に答える
0

いいえ -- C++ は、このような型システムの操作をサポートしていません。ある程度のランタイム リフレクションを備えた言語 (.NET など) でさえ、このパラダイムを正確にサポートしていません。それを行うには、はるかに動的な言語が必要です。

于 2008-10-24T22:05:22.250 に答える