C ++ 11にはC#スタイルのプロパティがありますか?


93

C#には、ゲッターとセッターを持つフィールド用の素晴らしい構文シュガーがあります。さらに、私は私が書くことを可能にする自動実装されたプロパティが好きです

public Foo foo { get; private set; }

C ++では私は書く必要があります

private:
    Foo foo;
public:
    Foo getFoo() { return foo; }

C ++ 11にはそのような概念があり、これに構文糖を付けることができますか?


62
これは、いくつかのマクロで実行できます。恥ずかしくて逃げる
マンカルス

7
@Eloff:すべてを公開することは常に悪い考えです。
Kaiserludi 2014年

8
そのような概念はありません!そして、あなたもそれを必要としません:seanmiddleditch.com/why-c-does-not-need-c-like-properties
CinCout

2
a)この質問はかなり古いものですb)私は構文シュガーを求めていました、それで括弧を取り除くことができますc)C ++がプロパティを適合させることに対して有効な引数を提示しますが、C ++はプロパティを「するかしないか」非常に主観的です。C ++はTouring-machineと同等ですが、C ++の生産性が向上するという意味ではありません。
Radim Vansa、2015年

3
絶対にありません。

回答:


87

C ++では、独自の機能を作成できます。名前のないクラスを使用したプロパティの実装例を次に示します。ウィキペディアの記事

struct Foo
{
    class {
        int value;
        public:
            int & operator = (const int &i) { return value = i; }
            operator int () const { return value; }
    } alpha;

    class {
        float value;
        public:
            float & operator = (const float &f) { return value = f; }
            operator float () const { return value; }
    } bravo;
};

独自のゲッターとセッターを適切に作成できます。また、ホルダークラスメンバーのアクセスが必要な場合は、このサンプルコードを拡張できます。


1
このコードを変更して、Fooが内部的にアクセスできるプライベートメンバー変数を保持する方法についてのアイデアはありますが、パブリックAPIはプロパティのみを公開していますか?もちろん、Fooをalpha / betaの友達にすることもできますが、値にアクセスするには、alpha.valueを書き込む必要がありますが、Fooの内部からメンバー変数に直接アクセスする方が、特別にネストされたプロパティクラスのメンバーではなく、Foo自体のメンバーにアクセスする。
Kaiserludi 2014年

1
@Kaiserludiはい:この場合、アルファとブラボーをプライベートにします。Fooでは、上記の「プロパティ」を使用して読み取り/書き込みを行うことができますが、Fooの外部では、これはもう不可能です。それを回避するには、公開されているconst参照を作成します。外部からアクセスできますが、常に参照しているため、読み取り専用です。唯一の注意点は、パブリックconst参照には別の名前が必要になることです。個人的_alphaにはプライベート変数とalpha参照に使用します。
カピチュ

1
@Kapichu:2つの理由から、解決策ではありません。1)C#プロパティでは、ゲッター/セッターは、メンバー関数が値に直接アクセスできるようにしながら、クラスのパブリックユーザーに強制される安全性チェックを埋め込むためによく使用されます。2)const参照は無料ではありませんsizeof(Foo)。コンパイラ/プラットフォームによっては、拡大されます。
ceztko

@psx:このアプローチの制限のため、それが表示される場合は、それを避けて、標準への適切な追加を待ちます。
ceztko

@Kapichu:ただし、コード例のアルファとブラボーはプロパティです。プロパティを使用する必要なしに、Fooの実装の内部から変数自体に直接アクセスしますが、APIのプロパティを介したアクセスのみを公開します。
カイザールディ2015

53

C ++にはこれが組み込まれていません。プロパティ機能を模倣するテンプレート定義できます

template <typename T>
class Property {
public:
    virtual ~Property() {}  //C++11: use override and =default;
    virtual T& operator= (const T& f) { return value = f; }
    virtual const T& operator() () const { return value; }
    virtual explicit operator const T& () const { return value; }
    virtual T* operator->() { return &value; }
protected:
    T value;
};

プロパティ定義するには

Property<float> x;

カスタムゲッター/セッターを実装するには、単に継承します:

class : public Property<float> {
    virtual float & operator = (const float &f) { /*custom code*/ return value = f; }
    virtual operator float const & () const { /*custom code*/ return value; }
} y;

読み取り専用プロパティを定義するには:

template <typename T>
class ReadOnlyProperty {
public:
    virtual ~ReadOnlyProperty() {}
    virtual operator T const & () const { return value; }
protected:
    T value;
};

そしてそれをクラスで使うにはOwner

class Owner {
public:
    class : public ReadOnlyProperty<float> { friend class Owner; } x;
    Owner() { x.value = 8; }
};

上記のいくつかをマクロで定義して、より簡潔にすることができます。


これがゼロコストの機能にコンパイルされるかどうか知りたいのですが、たとえば、各データメンバーをクラスインスタンスでラップすると、同じ種類の構造体パッキングになるかどうかはわかりません。

1
"カスタムゲッター/セッター"ロジックは、ラムダ関数を使用して構文的にクリーンにすることができますが、残念ながら、C ++で実行可能なコンテキストの外側にラムダを定義することはできません(まだ!)。あいまいなゲッター/セッターと同じくらい手間がかかります。

2
最後の例の「class:...」は興味深いものであり、他の例にはありません。新しいクラス名を導入することなく、必要なフレンド宣言を作成します。
Hans Olsson

これと2010年11月19日の回答の大きな違いは、これにより、ケースバイケースでゲッターまたはセッターをオーバーライドできるようになることです。そのようにして、入力が範囲内にあるか、変更通知を送信して変更イベントリスナーに送信するか、またはブレークポイントをぶら下げることができます。
Eljay

28

C ++言語には、すべてのプラットフォームとコンパイラで機能するものはありません。

しかし、あなたは、クロスプラットフォームの互換性を壊し、あなたは、例えば構文を使用することができるかもしれ特定のコンパイラにコミットして喜んでいる場合は、マイクロソフトのVisual C ++にあなたが行うことができます

// declspec_property.cpp  
struct S {  
   int i;  
   void putprop(int j) {   
      i = j;  
   }  

   int getprop() {  
      return i;  
   }  

   __declspec(property(get = getprop, put = putprop)) int the_prop;  
};  

int main() {  
   S s;  
   s.the_prop = 5;  
   return s.the_prop;  
}

2
これはclang
Passer By

18

あなたは、専用のタイプの部材を有するとオーバーライドすることで、ある程度ゲッターとセッターをエミュレートすることができますoperator(type)し、operator=それのために。それが良いアイデアかどうかは別の質問であり、私はそれ+1について私の意見を表明するためにKerrek SBの答えに行きます:)


そのようなタイプによる割り当てまたは読み取り時のメソッドの呼び出しをエミュレートできますが、割り当て操作を呼び出す人を区別できません(フィールド所有者でない場合にそれを禁止するため)-別のアクセスを指定して何をしようとしているのかゲッターとセッターのレベル。
Radim Vansa、2011

@Flavius:friendフィールドの所有者にa を追加するだけです。
kennytm

17

たぶん私が最後の数時間にアセンブルしたプロパティクラスを見てくださいhttps : //codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like-class-properties

これにより、次のように動作するプロパティを持つことができます。

CTestClass myClass = CTestClass();

myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);

しかし、これはユーザー定義のアクセサーを許可しますが、私が探していたパブリックゲッター/プライベートセッター機能はありません。
Radim Vansa 2012年

17

C ++ 11では、プロパティクラステンプレートを定義して、次のように使用できます。

class Test{
public:
  Property<int, Test> Number{this,&Test::setNumber,&Test::getNumber};

private:
  int itsNumber;

  void setNumber(int theNumber)
    { itsNumber = theNumber; }

  int getNumber() const
    { return itsNumber; }
};

そして、これがPropertyクラステンプレートです。

template<typename T, typename C>
class Property{
public:
  using SetterType = void (C::*)(T);
  using GetterType = T (C::*)() const;

  Property(C* theObject, SetterType theSetter, GetterType theGetter)
   :itsObject(theObject),
    itsSetter(theSetter),
    itsGetter(theGetter)
    { }

  operator T() const
    { return (itsObject->*itsGetter)(); }

  C& operator = (T theValue) {
    (itsObject->*itsSetter)(theValue);
    return *itsObject;
  }

private:
  C* const itsObject;
  SetterType const itsSetter;
  GetterType const itsGetter;
};

2
どういうC::*意味?こんなの見たことない?
Rika

1
これは、クラスの非静的メンバー関数へのポインターCです。これは単純な関数ポインターに似ていますが、メンバー関数を呼び出すには、関数が呼び出されるオブジェクトを提供する必要があります。これはitsObject->*itsSetter(theValue)、上記の例の行で実現されます。この機能の詳細については、こちらをご覧ください。
ChristophBöhme18年

@Niceman、使用例はありますか?会員になるには非常に費用がかかるようです。静的メンバーとしても特に有用ではありません。
Grim Fandango

16

他の多くの人がすでに言ったように、言語には組み込みのサポートはありません。ただし、Microsoft C ++コンパイラをターゲットにしている場合は、ここに記載されているプロパティのMicrosoft固有の拡張機能を利用できます。

これはリンクされたページの例です:

// declspec_property.cpp
struct S {
   int i;
   void putprop(int j) { 
      i = j;
   }

   int getprop() {
      return i;
   }

   __declspec(property(get = getprop, put = putprop)) int the_prop;
};

int main() {
   S s;
   s.the_prop = 5;
   return s.the_prop;
}

12

いいえ、C ++にはプロパティの概念はありません。getThis()またはsetThat(value)を定義して呼び出すのは面倒ですが、一部の機能が発生する可能性があることをこれらのメソッドのコンシューマーに通知しています。一方、C ++でフィールドにアクセスすると、追加のまたは予期しない機能が発生しないことがコンシューマに通知されます。一見するとプロパティアクセスはフィールドのように反応するように見えますが、実際にはメソッドのように反応するため、プロパティはこれをあまりわかりにくくします。

余談ですが、私は.NETアプリケーション(非常によく知られたCMS)で作業して、顧客のメンバーシップシステムを作成しようとしていました。ユーザーオブジェクトのプロパティの使用方法が原因で、予期しないアクションが発生し、実装が無限再帰を含む奇妙な方法で実行されました。これは、StreetAddressなどの単純なものにアクセスしようとすると、ユーザーオブジェクトがデータアクセスレイヤーまたはグローバルキャッシングシステムを呼び出したためです。彼らのシステム全体は、私がプロパティの乱用と呼ぶものに基づいています。プロパティの代わりにメソッドを使用していれば、何が問題だったのかをもっと早く理解できただろうと思います。フィールドを使用した場合(または少なくともプロパティをフィールドのように動作させる場合)、システムの拡張と保守が容易になったと思います。

[編集]私の考えを変えた。私は悪い日を過ごしたし、怒りに少し行った。このクリーンアップはより専門的なものにする必要があります。



4

これは厳密にはプロパティではありませんが、必要なことを簡単な方法で実行します。

class Foo {
  int x;
public:
  const int& X;
  Foo() : X(x) {
    ...
  }
};

ここで、大きなXはpublic int X { get; private set; }C#構文のように動作します。本格的なプロパティが必要な場合は、ここに実装するための最初のショットを作成しまし


2
これは良い考えではありません。このクラスのオブジェクトのコピーを作成するときは常にX、新しいオブジェクトの参照は古いオブジェクトのメンバーを指します。これは、ポインターメンバーのようにコピーされるだけだからです。これ自体は悪いですが、古いオブジェクトが削除されると、その上にメモリ破損が発生します。これを機能させるには、独自のコピーコンストラクター、代入演算子、および移動コンストラクターも実装する必要があります。
トースター

4

あなたはおそらくそれを知っていますが、私は単に次のようにします:

class Person {
public:
    std::string name() {
        return _name;
    }
    void name(std::string value) {
        _name = value;
    }
private:
    std::string _name;
};

このアプローチはシンプルで、巧妙なトリックを使用せず、仕事を完了させます!

ただし、プライベートフィールドの先頭にアンダースコアを付けたくない人もいるため、この方法は実際には使用できませんが、この方法を使用する人にとっては簡単です。:)

getおよびsetプレフィックスはAPIを明確にしませんが、それらをより詳細にし、有用な情報を追加しないと思う理由は、APIが理にかなっている場合に誰かがAPIを使用する必要があるとき、彼女はおそらくそれを理解するためです接頭辞なしで行います。

もう1つname、動詞ではないため、これらがプロパティであることを理解するのは簡単です。

最悪のシナリオ、APIが一貫していて、その人がname()アクセサーでありname(value)ミューテーターであることに気付かなかった場合、パターンを理解するためにドキュメントで一度だけルックアップする必要があります。

C#が大好きなのと同じくらい、C ++にはプロパティがまったく必要ないと思います!


ミューテーターはfoo(bar)(遅いの代わりにfoo = bar)使用する場合に意味がありますが、アクセサーはプロパティとはまったく関係ありません...
Matthias

@Matthiasプロパティとは何の関係もないと述べ、何も言わないので、詳しく説明してください。さらに、それらを比較しようとはしませんでしたが、ミューテーターとアクセサーが必要な場合は、この規則を使用できます。
Eyal Solnik、2017年

問題は、プロパティのソフトウェアの概念についてです。プロパティは、パブリックデータメンバー(使用法)のように使用できますが、実際には、アクセサー(宣言)と呼ばれる特別なメソッドです。あなたの解決策は、宣言と使用法のメソッド(通常のゲッター/セッター)に固執します。したがって、これはまず第一に、OPが要求する使用法ではなく、奇妙で型破りな命名規則です(したがって、構文上の砂糖もありません)。
Matthias

C ++ 1つの最善の初期化中であるため、マイナーの副作用として、あなたのミューテータは意外にも、プロパティとして働くfoo(bar)代わりにfoo=barして達成することができるvoid foo(Bar bar)のミューテータメソッド_fooメンバ変数。
Matthias

@Matthias私はプロパティとは何かを知っています。私はC ++とC#を10年以上書いてきました。プロパティの利点とそれらが何であるかについては議論していませんが、実際にはC ++でそれらを必要としていません「それらはパブリックデータとして使用できることを言っていますが、ほとんどの場合それは真実ですが、C#では、参照によってプロパティを渡すのに、パブリックフィールドを使用してプロパティを直接渡すことができない場合もあります。
Eyal Solnik、2017年

4

いいえ。しかし、それが単にget:set関数であり、get:setメソッド内で実行される追加のタスクがない場合は、それを単に公開するかどうかを検討する必要があります。


2

私は複数のC ++ソースからアイデアを集め、それをC ++のゲッター/セッターの素晴らしい、まだ非常に単純な例に入れました。

class Canvas { public:
    void resize() {
        cout << "resize to " << width << " " << height << endl;
    }

    Canvas(int w, int h) : width(*this), height(*this) {
        cout << "new canvas " << w << " " << h << endl;
        width.value = w;
        height.value = h;
    }

    class Width { public:
        Canvas& canvas;
        int value;
        Width(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } width;

    class Height { public:
        Canvas& canvas;
        int value;
        Height(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } height;
};

int main() {
    Canvas canvas(256, 256);
    canvas.width = 128;
    canvas.height = 64;
}

出力:

new canvas 256 256
resize to 128 256
resize to 128 64

ここでオンラインでテストできます:http : //codepad.org/zosxqjTX


自己屈折を維持するためのメモリオーバーヘッドがあり、+厄介なctor構文。
Red.Wave

プロパティを提案するには?そのような提案が却下されたと思います。
Red.Wave

@ Red.Wave拒否の支配者と主人に首を下げなさい。C ++へようこそ。自己参照が必要ない場合は、ClangおよびMSVCにプロパティのカスタム拡張機能があります。
lama12345

お辞儀はできません。すべての機能が適切であるとは限りません。私にとってオブジェクトは、setter + getter関数のペア以上のものです。不要な永続メモリのオーバーヘッドを回避して独自の実装を試みましたが、インスタンスを宣言する構文と雑用は満足のいくものではありませんでした。私は宣言的なマクロを使用することに惹かれましたが、とにかくマクロの大ファンではありません。そして、ついに私のアプローチは、関数構文でアクセスされる磁器につながりました。私を含む多くの人はそれを承認していません。
Red.Wave

0

あなたのクラスは本当にいくつかの不変式を強制する必要がありますか、それとも単にメンバー要素の論理的なグループ化ですか?後者の場合は、構造体にしてメンバーに直接アクセスすることを検討してください。


0

ここに書かれたマクロのセットがあります。THisには、値型、参照型、読み取り専用型、強い型と弱い型の便利なプロパティ宣言があります。

class MyClass {

 // Use assign for value types.
 NTPropertyAssign(int, StudentId)

 public:
 ...

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.