【问题标题】:How to access instance variables in C++ like Objective-C getter and setter如何在 C++ 中访问实例变量,如 Objective-C getter 和 setter
【发布时间】:2013-04-09 12:53:30
【问题描述】:

以下是一个例子:

在 circle.h

class circle
{
    double _radius;
public:
    double getRadius() {return _radius;}
    void setRadius(double r) {_radius=r;} 
}

在主目录

int main()
{
    circle a;
    cout<<a.getRadius();    // I want to use "a.radius"
    a.setRadius(3.2);       // I want to use "a.radius=3.2"
}

所以第一个问题是为什么我们应该使用 get 和 set 函数来访问实例变量而不是直接访问它们?第二个问题是如何做运算符重载,让get和set的函数调用看起来简洁(就像objective-c那样)?

【问题讨论】:

  • 为什么我们应该使用get和set函数来访问实例变量而不是直接访问它们?”有书章节关于值封装。这可能超出了 Stack Overflow 的范围。

标签: c++ objective-c class operator-overloading


【解决方案1】:

当然,使您希望的语法可用的最简单方法是让 _radius 成为一个名为 radius 的公共成员:

class circle
{
public:
    double radius;
}

现在你可以像这样使用它了:

circle c;
c.radius = 10.0;

但是,您不应仅根据您希望如何编写代码(或者您可能如何用 C# 编写代码)来做出此类决定。我建议您为每个成员考虑以下几点:

  1. 直接设置成员有意义吗?也许成员只是对象状态的私有部分,并受其他成员函数的影响。考虑下面的例子,你真正想要暴露的接口只是一个增加circle大小的函数:

    class circle
    {
    public:
        circle(double radius, double delta = 1.0)
          : radius(radius), delta(delta) { }
    
        double increaseSize() {
          radius += delta;
          return radius;
        }
    private:
        double radius;
        double delta;
    }
    
  2. 您需要强制执行不变量吗?考虑一下,也许,您已经决定不可能有像 circle 这样的东西,其半径为负。您可能希望通过使用成员函数检查给定的新半径以确保它大于 0 来强制执行此操作。

  3. 您想通过界面提供一些不同的成员表示吗?也许您希望circle 保持其作为成员的半径,但任何客户唯一想知道的是它的区域。您可以将radius 设为私有,然后提供getArea 函数,如下所示:

    class circle
    {
    public:
        circle(double radius)
          : radius(radius) { }
    
        double getArea() {
          return PI * radius * radius;
        }
    private:
        double radius;
    }
    
  4. 上述几点可能目前都不适用,但将来可能会适用。如果您有这种远见,最好现在提供 getter 和 setter 函数。这样可以确保客户端使用的接口以后不会更改。

作为 getter 和 setter 的替代命名方案,简单地拥有两个重载函数是很常见的 - 一个用于设置,一个用于获取 - 如下所示:

class circle
{
public:
    void radius(double); // Set the radius
    double radius();     // Get the radius
private:
    double radius;
}

【讨论】:

    【解决方案2】:

    回答第一个问题:因此您的对象已被封装,您可以处理更改的内部表示或在半径更改时需要修改/更新。

    对于第二个:你不能。 C++ 不支持这样的对象获取器和设置器。

    (实际上几乎是可能的,但确实不值得)

    【讨论】:

      【解决方案3】:
      1. 使用 setter 和 getter 有多种原因;

        • 它们提供更好的封装;类的内部变量的实际值应该是该类设置/获取的职责,不是类实例用户的职责。但是在这个问题上有不同的思想流派。
        • 它们允许在调用 setter/getter 时做更多的事情,而不仅仅是设置/获取一个值。例如,

          class circle
          {
              double _radius;
              double _perimeter;
          
          public:
          
              double getRadius() {return _radius;}
          
              void setRadius(double r) {
                      _radius = r;
                      _perimeter = 2*M_PI*r;
              } 
          };
          
      2. 好吧,您所指出的方式可以简单地通过将radius 设为公共变量来完成 :) 但是,请考虑这种替代方法:

        class circle
        {
        private: 
            double _radius;
        
        public:
            const double &radius;
        
            circle(double R)
                : radius(R)
                : radius(_radius)   
            {}
        
            void setRadius(double r) {
                _radius = r;
            } 
        };
        

        然后会像这样使用:

        cout << a.radius;    // works
        a.radius = 3.2;      // produces error
        a.setRadius(3.2);    // works
        

        这种方法的好处是简洁、可能更直观的 getter(即使用实际的变量名称)、更简洁的代码(不需要无数几乎空的 getter),甚至可能提高性能(调用 getter 函数比直接访问变量要慢,尽管这取决于编译器优化设置)。

      这里提到的所有方法都有优点和缺点。最后,这真的完全取决于您的团队/雇主的偏好。

      【讨论】:

        【解决方案4】:

        您使用 getter 和 setter 来实现更好的封装,并可以更轻松地更改底层实现。

        关于第二个问题:你不能那样做。您将不得不编写一个 getter 和 setter 方法。

        【讨论】:

          【解决方案5】:

          还有初始化方法:

          OCD::OCD ( ) : _number( 0 )
          {
          }
          

          和in body构造函数方式:

          OCD::OCD ( size_t initial_value )
          {
            _number = initial_value;
          }
          

          要在类实例中访问它们,只需使用变量名:

            _number = value;
          

          但如果你有一个同名的全局变量、局部变量或参数变量,你可以像这样具体:

            this->_number = value;
          

          然后你可以从实例外部调用它:

            // (that is if _number was public)
            std::cout << "ocd2._number    => " << ocd2._number << std::endl;
            std::cout << "ocd3p->_number  => " << ocd3p->_number << std::endl;
          

          方法也一样:

            std::cout << "ocd2.get( )     => " << ocd2.get() << std::endl;
            std::cout << "ocd3p->get( )   => " << ocd3p->get() << std::endl;
          

          请参阅我关于该主题的要点:https://gist.github.com/LaughingSun/7d2330ccff0777bec106

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-02
            • 2011-07-26
            • 1970-01-01
            • 2015-04-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多