【问题标题】:Does C++11 have C#-style properties?C++11 有 C# 风格的属性吗?
【发布时间】:2012-01-12 04:50:19
【问题描述】:

在 C# 中,具有 getter 和 setter 的字段有一个很好的语法糖。此外,我喜欢允许我编写的自动实现的属性

public Foo foo { get; private set; }

在 C++ 中我必须编写

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

C++11 中是否有一些这样的概念允许我对此有一些语法糖?

【问题讨论】:

  • 这可以用几个宏来完成。 羞愧逃跑
  • @Eloff:公开所有内容总是一个坏主意。
  • 没有这个概念!你也不需要它:seanmiddleditch.com/why-c-does-not-need-c-like-properties
  • a) 这个问题已经很老了 b) 我要求语法糖,这样我就可以摆脱括号 c) 尽管文章提出了反对调整属性的有效论据,无论是 C++ “需要或不需要”属性是非常主观的。即使没有它们,C++ 也等同于 Touring-machine,但这并不意味着拥有这样的语法糖会让 C++ 更有效率。
  • 绝对不是。

标签: c# c++ class c++11


【解决方案1】:

在 C++ 中,您可以编写自己的功能。 这是使用未命名类的属性的示例实现。 Wikipedia article

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;
};

您可以编写自己的 getter 和 setter,如果您想要持有者类成员访问权限,您可以扩展此示例代码。

【讨论】:

  • 关于如何修改此代码以仍然具有 Foo 可以在内部访问的私有成员变量,而公共 API 仅公开属性的任何想法?我当然可以让 Foo 成为 alpha/beta 的朋友,但是我仍然必须编写 alpha.value 来访问该值,但我宁愿直接从 Foo 内部访问成员变量感觉更像访问 Foo 本身的成员,而不是特殊嵌套属性类的成员。
  • @Kaiserludi 是的:在这种情况下,将 alpha 和 bravo 设为私有。在 Foo 中,您可以使用上面的那些“属性”读/写,但在 Foo 之外,这将不再可能。为了避免这种情况,请创建一个公开的 const 引用。它可以从外部访问,但仅用于阅读,因为它是一个恒定的参考。唯一需要注意的是,公共 const 引用需要另一个名称。我个人会使用_alpha 作为私有变量,alpha 作为参考。
  • @Kapichu:不是解决方案,原因有两个。 1) 在 C# 中,属性 getter/setter 通常用于嵌入安全检查,这些安全检查被强制用于类的公共用户,同时允许成员函数直接访问值。 2) const 引用不是免费的:取决于编译器/平台,它们会放大sizeof(Foo)
  • @psx:由于这种方法的局限性,我会避免它并等待适当添加到标准中,如果它会出现的话。
  • @Kapichu:但是示例代码中的 alpha 和 bravo 是属性。我想直接从 Foo 的实现内部访问变量本身,而不需要使用属性,而我只想通过 API 中的属性公开访问。
【解决方案2】:

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;

要实现自定义getter/setter,只需继承:

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; }
};

您可以在 ma​​cros 中定义上述部分内容以使其更简洁。

【讨论】:

  • 我很好奇这是否可以编译成零成本的特性,我不知道将每个数据成员包装在一个类实例中是否会导致相同的结构打包,例如。
  • “自定义 getter/setter”逻辑可以通过使用 lambda 函数在语法上变得更简洁,不幸的是,您不能在 C++ 中的可执行上下文之外定义 lambda(但是!)所以不使用预处理器宏最终得到的代码就像愚蠢的 getter/setter 一样繁琐,这很不幸。
  • 最后一个示例中的“class: ...”很有趣,但在其他示例中却没有。它创建了必要的朋友声明——不引入新的类名。
  • 这与 2010 年 11 月 19 日的答案之间的一个很大区别是,这可以根据具体情况覆盖 getter 或 setter。这样就可以检查输入是否在范围内,或者发送更改通知以更改事件侦听器,或者挂断点的位置。
  • 请注意,virtual 对于大多数用例来说可能是不必要的,因为不太可能以多态方式使用属性。
【解决方案3】:

C++ 语言中没有任何东西可以跨所有平台和编译器工作。

但如果你愿意打破跨平台兼容性并提交给特定的编译器,你也许可以使用这样的语法,例如 in Microsoft 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;  
}

【讨论】:

  • 这也是works with clang
  • 很好的答案!我也很感谢您指出破坏功能的主要风险。
【解决方案4】:

您可以通过拥有一个专用类型的成员并覆盖operator(type)operator= 来在某种程度上模拟getter 和setter。这是否是一个好主意是另一个问题,我将在+1Kerrek SB 的回答中表达我对此的看法:)

【讨论】:

  • 您可以通过这种类型模拟在分配或读取时调用方法,但是您无法区分谁调用了分配操作(如果不是字段所有者则禁止它)-我我试图通过为 getter 和 setter 指定不同的访问级别来做到这一点。
  • @Flavius:只需将friend 添加到字段所有者即可。
【解决方案5】:

使用 C++11,您可以定义一个 Property 类模板并像这样使用它:

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; }
};

这里是属性类模板。

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;
};

【讨论】:

  • C::* 是什么意思?我以前从未见过这样的东西?
  • 它是一个指向类C中的非静态成员函数的指针。这类似于普通函数指针,但为了调用成员函数,您需要提供一个调用该函数的对象。这是通过上面示例中的itsObject-&gt;*itsSetter(theValue) 行实现的。有关此功能的更详细说明,请参阅here
  • @Niceman,有使用示例吗?成为会员似乎非常昂贵。作为静态成员,它也不是特别有用。
【解决方案6】:

也许看看我在过去几个小时组装的属性类: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);

【讨论】:

  • 很好,但是尽管这允许用户定义的访问器,但没有我正在寻找的公共 getter/private setter 功能。
【解决方案7】:

正如许多其他人已经说过的那样,该语言没有内置支持。但是,如果您的目标是 Microsoft C++ 编译器,则可以利用 Microsoft 特定的属性扩展,该扩展已记录在 here.

这是来自链接页面的示例:

// 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;
}

【讨论】:

    【解决方案8】:

    不,C++ 没有属性的概念。尽管定义和调用 getThis() 或 setThat(value) 可能很尴尬,但您正在向这些方法的使用者声明可能会出现某些功能。另一方面,在 C++ 中访问字段会告诉消费者不会出现额外的或意外的功能。属性会使这一点变得不那么明显,因为属性访问乍一看似乎反应像一个字段,但实际上反应像一个方法。

    顺便说一句,我在一个 .NET 应用程序(一个非常著名的 CMS)中工作,试图创建一个客户会员系统。由于他们为用户对象使用属性的方式,我没有预料到的动作正在触发,导致我的实现以奇怪的方式执行,包括无限递归。这是因为他们的用户对象在尝试访问 StreetAddress 之类的简单事物时调用了数据访问层或某些全局缓存系统。他们的整个系统都是建立在我所谓的财产滥用之上的。如果他们使用方法而不是属性,我想我会更快地找出问题所在。如果他们使用了字段(或者至少让他们的属性表现得更像字段),我认为系统会更容易扩展和维护。

    [编辑] 改变了我的想法。我有一个糟糕的一天,有点咆哮。这个清理应该更专业。

    【讨论】:

      【解决方案9】:

      基于https://stackoverflow.com/a/23109533/404734,这是一个带有公共 getter 和私有 setter 的版本:

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

      【讨论】:

        【解决方案10】:

        您可能知道,但我会简单地执行以下操作:

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

        这种方法很简单,不使用任何巧妙的技巧,并且可以完成工作!

        但问题是有些人不喜欢在私有字段前加上下划线,所以他们不能真正使用这种方法,但幸运的是,对于那些这样做的人来说,这真的很简单。 :)

        get 和 set 前缀不会增加 API 的清晰度,但会使它们更加冗长,我不认为它们添加有用信息的原因是因为当有人需要使用 API 时,如果 API 有意义,她可能会意识到没有前缀的情况。

        还有一点,很容易理解这些是属性,因为name 不是动词。

        最坏的情况是,如果 API 是一致的并且该人没有意识到 name() 是一个访问器,而 name(value) 是一个突变器,那么她只需在文档中查找一次即可了解该模式.

        尽管我很喜欢 C#,但我认为 C++ 根本不需要属性!

        【讨论】:

        • 如果使用 foo(bar)(而不是较慢的 foo = bar),你的 mutators 是有意义的,但你的访问器与属性完全无关......
        • @Matthias 说它与属性完全没有关系,什么也没告诉我,你能详细说明一下吗?除此之外,我没有尝试比较它们,但如果你需要一个 mutator 和 accessor,你可以使用这个约定。
        • 问题是关于Properties的软件概念。属性可以像公共数据成员一样使用(用法),但它们实际上是称为访问器(声明)的特殊方法。您的解决方案坚持声明和使用的方法(普通 getter/setter)。因此,首先,这绝对不是 OP 要求的用法,而是一些奇怪且非常规的命名约定(因此,其次,也没有语法糖)。
        • 作为一个次要的副作用,您的 mutator 出人意料地作为一个属性工作,因为在 C++ 中,最好使用 foo(bar) 而不是 foo=bar 进行初始化,这可以通过在 a 上的 void foo(Bar bar) mutator 方法来实现_foo 成员变量。
        • @Matthias 我知道什么是属性,我已经写了很多 C++ 和 C# 十多年了,我并不是在争论属性的好处以及它们是什么,但我从来没有真正在 C++ 中需要它们,实际上您是说它们可以用作公共数据,这几乎是正确的,但是在 C# 中,您甚至不能直接使用属性,例如通过 ref 传递属性而使用公共字段,你可以。
        【解决方案11】:

        这不完全是一个属性,但它以简单的方式做你想做的事:

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

        这里的大 X 的行为类似于 C# 语法中的 public int X { get; private set; }。如果你想要完整的属性,我第一次尝试实现它们here

        【讨论】:

        • 这不是一个好主意。每当您复制此类的对象时,新对象的引用X 仍将指向旧对象的成员,因为它只是像指针成员一样被复制。这本身就很糟糕,但是当旧对象被删除时,你会在它上面得到内存损坏。为了使这项工作,您还必须实现自己的复制构造函数、赋值运算符和移动构造函数。
        【解决方案12】:

        不.. 但是您应该考虑它是否只是 get : set 函数并且在 get:set 方法中没有执行其他任务只是将其公开。

        【讨论】:

          【解决方案13】:

          我从多个 C++ 资源中收集了这些想法,并将其放入一个不错的、仍然非常简单的 C++ 中的 getter/setter 示例:

          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

          【讨论】:

          • 有一个内存开销来保持自引用,+ 一个 akward ctor 语法。
          • 要提议房产?我想已经有很多这样的提议被拒绝了。
          • @Red.Wave 向拒绝的领主和大师鞠躬。欢迎使用 C++。如果您不想要自引用,Clang 和 MSVC 对属性有自定义扩展。
          • 我永远不能低头。我认为并非每个功能都适用。对我来说,对象不仅仅是一对 setter+getter 函数。我已经尝试过自己的实现来避免不必要的永久内存开销,但是声明实例的语法和杂务并不令人满意;我被使用声明性宏所吸引,但无论如何我并不是宏的忠实粉丝。最后,我的方法导致使用函数语法访问属性,包括我自己在内的许多人都不赞成。
          【解决方案14】:

          你的类真的需要强制执行一些不变量还是只是成员元素的逻辑分组?如果是后者,您应该考虑将事物设为结构并直接访问成员。

          【讨论】:

            【解决方案15】:

            有一组宏写成Here。这对值类型、引用类型、只读类型、强类型和弱类型有方便的属性声明。

            class MyClass {
            
             // Use assign for value types.
             NTPropertyAssign(int, StudentId)
            
             public:
             ...
            
            }
            

            【讨论】:

              【解决方案16】:

              这是 c++ 中属性的另一种实现 (std=c++17)

              propertycpp@github

              它提供了一个示例,展示了如何创建属性,或者以只读方式公开提供,在尝试写入属性时处理构建时异常;显示具有读写访问权限的“默认”属性的示例、动态 getter 示例(更改实际值)和动态只读。

              初始化一个属性远非好事......

              【讨论】:

                猜你喜欢
                • 2014-10-20
                • 2012-11-06
                • 1970-01-01
                • 2011-05-09
                • 1970-01-01
                • 1970-01-01
                • 2011-08-29
                • 2015-11-26
                • 1970-01-01
                相关资源
                最近更新 更多