【问题标题】:C++ practice similar to C#'s read only data member behaviorC++实践类似C#的只读数据成员行为
【发布时间】:2013-11-09 00:28:21
【问题描述】:

我学了一点 C#,现在我正在学习 C++。在 C# 中,数据隐藏可以使用 get 和 set 运算符来完成,通过提供“get”而不是“set”,可以将数据成员呈现为“只读”。

这将允许一个类 (Person) 包含另一个类 (Account),这样 Account 类的公共功能对 Person.Account 的用户可用,但用户不能直接更改 Account 类,因为它是只读的。

这应该在下面的代码示例中更清楚。

我的问题是,由于 C++ 不提供漂亮的 get/set 语法,是否有与下面代码类似的 C++?

using System;

class Person
{
    private string _Name;
    public string Name { get { return _Name; } set { _Name = value; } }

    private Account _Account;
    public Account Account { get { return _Account; } }

    public Person()
    {
        _Name = "";
        _Account = new Account();
    }
}

class Account
{
    private decimal _Balance;
    public decimal Balance { get { return _Balance; } }

    public Account()
    {
        _Balance = 0;
    }
    public void Deposit(decimal deposit)
    {
        _Balance += deposit;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person p = new Person();
        p.Name = "John Doe";

        // not allowed: p.Account = new Account();
        // Property or indexer 'CSharp.Person.Account' cannot be assigned to -- it is read only

        // allowed: the Account Object's public functions are available
        p.Account.Deposit(1000);

        Console.WriteLine(p.Account.Balance.ToString());
        // console says "1000"
    }
}

【问题讨论】:

  • 请注意,标准禁止在用户代码中使用下划线和大写字母作为名称。请参阅 C++11 17.6.4.3.2 [global.names]/1:某些名称和函数签名集始终保留给实现:每个名称包含双下划线 _ _ 或以下划线开头,后跟大写字母 (2.12) 保留给实现以供任何使用。
  • 注意到了。我正在尝试为 C++ 中的私有属性找到我喜欢的样式。我对 myAccount 不感兴趣,我更不喜欢 m_Account。 C++ 课的老师喜欢使用 myAccount、myPerson 等作为私有成员。

标签: c# c++ class object-oriented-analysis


【解决方案1】:

这些特殊成员在 C# 术语中称为“属性”,在 C++ 中没有直接等效的。您可以将成员设为公开或私有,并定义 getter 和 setter 方法,但您不会拥有 C# 的语法糖,您必须显式调用它们。

实际上,可以在 C++ 中编写这种行为,但它很难看,详见此处:http://www.cplusplus.com/forum/general/8147/

【讨论】:

    【解决方案2】:

    这两种语言之间存在根本的脱节。您说您希望该帐户是只读的,但随后您对其调用了修改函数 (Deposit)。我的 C++ 有线大脑说这是 gobbledygook。但当然,这是因为语言的不同。分配在两种语言中意味着完全不同的东西。在 C++ 中,它只是另一个修改操作。在 C# 中,它重新分配引用以指向不同的对象。 (我想,但我实际上并没有使用 C#,所以我可能是错的)。

    现在,我可能会离开这里,但我建议您在这种特殊情况下实际上不需要此功能。在我看来,它的唯一好处是保持Person.Account 引用的对象唯一。所以两个Person 对象不共享同一个Account 对象。但这在 C++ 中不是必需的。变量是对象。它们不是共享的,除非您使用工具明确地使它们共享(使用引用、指针或智能指针)。所以,你真正想要的只是一个公共成员。

    如果您真的只是想禁止分配,那么您可以这样做。只需删除Account 的赋值运算符即可。或者,在 C++11 之前,将其声明为私有且不提供任何实现。

    如果我不在这里,您能否更清楚地了解您要通过此实现什么?因为,再次说明(在 C++ 中)说您想要一个只读对象,但您想要修改它(您正在通过调用 Deposit)是没有任何意义的。我忽略了将余额成员设为mutable 的可能性,但这似乎不适合这种情况。

    【讨论】:

    • 假设 Account 有另一个属性 AccountID,它不能被修改。在 C++ 示例中,我认为您可以取消引用帐户对象并将其替换为新的/不同的帐户对象。您可以通过交换一个全新的 Account 实例来更改 AccountID 属性。在 C# 示例中,您不能这样做。我只是想了解 C++ 的工作原理。我想也许答案是我必须用公共 Person 函数包装所需的 Account 函数。
    • @MatthewBriggs:您可以将帐户 id 设为 const。这将禁用赋值运算符。在我看来,更好的解决方案是将 ID 存储在 Person 中。然后,有另一个结构(即std::map<int,Account>)将帐户ID号映射到帐户。
    【解决方案3】:

    C++ 似乎没有直接的等价物——它没有考虑到属性之类的东西,而 C# 是因为 java 已经实现了它。然而,和你一样,我想知道这是否可能,偶然发现了this little gem。基本上,它似乎是一种返回您想要的变量的方法 - 并将其设置为 const 以便您一旦拥有它就无法更改它,使其仅在您使用该方法时有效地读取访问它。因此,如果需要,可以使用类似的东西。实际上,如果您考虑一下,“属性”或“getter 和 setter”实际上只是链接中内容的实现 - setter 具有将局部变量设置为您想要的值的参数,并且 getter 返回它的只读版本。

    【讨论】:

      【解决方案4】:

      返回shared_ptr<Account>。这样,消费者可以调用 Account 的方法,但不能覆盖 Person._account。

      #include <iostream>
      #include <memory>
      
      using std::cout;
      using std::endl;
      using std::shared_ptr;
      using std::wstring;
      
      
      class Account
      {
        public:
          long Amount;
      };
      
      class Person
      {
        private:
          shared_ptr<Account> _account = shared_ptr<Account>(new Account());
      
        public:
          wstring Name;
          shared_ptr<Account> GetAccount() { return _account; }
          shared_ptr<const Account> GetAccount() const { return _account; } // a const Person has a const Account
      };
      
      int main()
      {
        Person p;
      
        shared_ptr<Account> a = p.GetAccount();
        a->Amount = 1;                          // access p.Account
        cout << a->Amount << endl;              // print 1
      
        Account* b = new Account();
        b->Amount = 2;
      
        a.reset(b);                             // doesn't affect p.Account
      
        cout << p.GetAccount()->Amount << endl; // still 1
      }
      

      这是可行的,因为返回共享指针会复制它,包括原始指针。对共享容器的更改仅影响副本,但取消引用底层指针会到达原始实例。

      【讨论】:

      • 我们还没有在 C++ 类中学习共享指针,但这似乎是根据您的陈述“消费者可以调用帐户的方法但不能覆盖 Person._account”的正确答案。正如@BenjaminLindley 指出的那样,我的 OO 概念在这里可能有点偏离。
      • 按 const 值返回是没有用的,并且会破坏许多潜在的编译器优化。此外,您应该将const 放在任何“getter”中,即任何不修改对象状态的函数中。
      • @Manu343726 经过一番研究,我同意返回const shared_ptr&lt;T&gt; 毫无意义。我还添加了一个单独的 getter,以便 const Person 将产生 shared_ptr&lt;const Account&gt;。将现有的 getter 标记为 const 将允许调用者修改 const Person 的帐户。
      猜你喜欢
      • 2011-06-05
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多