1

みなさん、良い一日を!

プロパティは C++ では実装されていません。Id est は書き込めません

myObject.property = value;   // try to set field f_ to value

これはpropertyプライベート データ メンバーです。パブリック データ メンバーは、OOP カプセル化規則に違反しています。

代わりに、getter/setter コードを記述する必要があります。

myObject.setField(value);    // try to set field f_ to value

この投稿は、C++ でのプロパティ エミュレーションに関するものです。プロパティ呼び出しはパブリック データ メンバー呼び出しのように見えますが、実際のプライベート データ メンバー値を設定または取得するためにカスタム UDF コードが使用されます。

私たちがやりたいことは、単純なインターフェイスの使用 (パブリック データ メンバーのような、関数呼び出し構文なし) を許可することですが、基になるゲッター/セッターは複雑になる可能性があります (データの割り当て/読み取り専用以上のことを行う)

次のコード (私の同僚が書いた) は、単純なプロパティの実装を示しています。

#include <iostream>

template<class C, class M>
inline C* get_parent_this(typename M C::*member_ptr, M*const member_this) 
{
    //interpret 0 as address of parent object C and find address of its member
    char* base   = reinterpret_cast<char*>(nullptr);
    char* member = reinterpret_cast<char*>( &(reinterpret_cast<typename C*>(base)->*member_ptr) );
    //modify member_this with offset = (member - base)
    return reinterpret_cast<typename C*>(reinterpret_cast<char*>(member_this) - (member - base) );
}

class Owner
{
    int x,pr_y;

    int   get_y()       {return pr_y;}
    void  set_y(int v)  {pr_y = v;}
public:
    struct
    {
        operator int()          { return get_parent_this(&Owner::y, this)->get_y(); }
        void operator =(int v)  { get_parent_this(&Owner::y, this)->set_y(v); } 
    } y;
};

int main ()
{   
    Owner ow;
    ow.y = 5;
    std::cout << ow.y;

    if( get_parent_this(&Owner::y, &ow.y) == &ow) std::cout << "OK\n";
    if( (char *)&ow.y != (char *)&ow) std::cout << "OK\n";

    return 0;
}

上記の例には、追加のジョブ (整合性チェックや境界チェックなど) を行わない単純な getter/setter のペアがありますが、このコードは、境界チェックや整合性チェックなどを実行するのが複雑になる場合があります。これは単なるテスト例です。

get_parent_thisヘルパー テンプレートの "奇妙な" コードについて心配する必要はありません。最も「恐ろしい」のはオフセット計算です。nullptr (NULL, 0x0) アドレスはスタブとして使用されます。このアドレスからの書き込みや読み取りは行いません。サブオブジェクトのアドレスに基づく所有者オブジェクトのオフセット計算にのみ使用します。0x0 の代わりに任意のアドレスを使用できます。だから、これは意味がありません。


プロパティの使用法:

  1. 誰かがパブリック データ メンバー プロパティを使用している場合は、1.1 の場合に役立ちます。何か問題が発生した場合にパブリック データ メンバーの呼び出しを追跡する。1.2. パブリック データ メンバーの使用に基づく従来のコードを簡単にアップグレードできます。

  1. C++ でのプロパティのエミュレーションについてどう思いますか? 活発な考えですか?このアイデアには欠点がありますか (見せてください)?
  2. サブジェクトアドレスからの所有者オブジェクトアドレスの計算についてどう思いますか? あなたが知っているテクニックと起こりうる落とし穴は何ですか?

あなたの考えを教えてください!

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

4

2 に答える 2

2

明らかな理由により、コードのコンパイルに失敗します。ほとんど typenameSは不要です。nullptr自作 だと思います#define(確かにC ++ 11nullptrではありません)。

これは、実際に何が起こっているのかを簡単に確認できるようにする、洗練されたコンパイルバージョンです。

#include <iostream>

template<class C, class M>
inline C* get_parent_this(M C::*member_ptr, M* const member_this) 
{
  C* base = NULL;
  // !!! this is the tricky bit !!!
  char* member = reinterpret_cast<char*>(&(base->*member_ptr));
  return reinterpret_cast<C*>(reinterpret_cast<char*>(member_this) - member );
}

class Owner
{
  int x, pr_y;
  virtual int   get_y()       {return pr_y;}
  void  set_y(int v)  {pr_y = v;}
public:
  struct
  {
    operator int()          { return get_parent_this(&Owner::y, this)->get_y(); }
    void operator =(int v)  { get_parent_this(&Owner::y, this)->set_y(v); } 
  } y;
};

トリッキーなビット:これには、nullポインターの逆参照が含まれます。これは、一部のコンパイラでマクロoffsetofが以前に定義されていた(そして現在も定義されている)方法とある程度同じです。stddef.hこれは一部のコンパイラで確実に機能しますが、未定義の動作です。このトピックに関するディスカッションへのリンクは次のとおりです。

ここで議論を繰り返す価値はないと思います。難読化された公開データ以外に、コードがあなたに何を買っているのかは本当にわかりません。あなたが本当に必要とするいくつかのケースでは、setter私はこれを使う代わりにそれを書くでしょう。コーディングスタイルで特異な理由で公開データが禁止されている場合は、一連のマクロを記述してそれらを定義します。

于 2012-02-07T23:09:03.113 に答える
2

参照オブジェクトはすでにワームの缶です。問題は次のとおりです。

  • それらは、次のような型の推論を妨害します。y = max(y, myobject.y)
  • オーバーロードできないoperator.ので、このようなクラスのプロキシはかなり面倒です
  • ある種の驚きswap(object1.y, object2.y)

あなたがそれを使用している方法には、追加の驚きがあります。たとえば、誰かがゲッターの副作用を呼び出したい場合、単に書き込むだけでmyobject.y;は機能しません。実際には、intへのキャストを呼び出す必要があります。

プロパティをエミュレートする必要があると思う理由を十分に説明できない限り、「これを行わないでください」以外のアドバイスが得られるとは想像できません。(それでも、アドバイスは、なぜ本当に必要ないのかを説明する方向に傾く可能性があります)

于 2012-02-08T07:29:13.333 に答える