如何制作一个好的设置/获取方法

How to make a good set/get method

本文关键字:设置 获取 方法 一个 何制作      更新时间:2023-10-16

例如,我们有这个类:

class Coord
{
  double x;
  double y;
  double z;
public:
  Coord() { x = y = z = 0; }
  void set(double xx, double yy, double zz)
  {
    x = xx;
    y = yy;
    z = zz;
  }
  void set_x(double xx) { x = xx; }
  void set_y(double yy) { y = yy; }
  void set_z(double zz) { z = zz; }
  double get_x() { return x; }
  double get_y() { return y; }
  double get_z() { return z; }
};

在这 7 种方法上,我们可以设置并获取坐标的 x,y 和 z。我有兴趣创建更少的方法set()get()我可以调用类似的东西:

int main()
{
  Coord c;
  c.set_x(5); /* only set x */
  c.set_y(6); /* or y */
  c.set_z(7); /* or z */
  c.set(1,2,5); /* or setting x, y and z */
  c.get_x(); /* only get x */
  c.get_y(); /* or y */
  c.get_z(); /* or z */
}

如果Coord类那么简单,它也可能是struct

无论如何,你可以写这样的东西:

class Coord
{
public:
  enum xyz {x = 0, y, z};
  Coord() : vec{x, y, z} {}
  template<xyz C> void set(double v) { vec[C] = v; }
  template<xyz C> double get() const { return vec[C]; }
  void set(double xx, double yy, double zz)
  {
    set<Coord::x>(xx);
    set<Coord::y>(yy);
    set<Coord::z>(zz);
  }
private:
  double vec[z + 1];
};

并通过以下方式使用类:

Coord c;
c.set<Coord::x>(5); /* only set x */
c.set<Coord::y>(6); /* or y */
c.set<Coord::z>(7); /* or z */
c.set(1,2,5); /* or setting x, y and z */
c.get<Coord::x>(); /* only get x */
c.get<Coord::y>(); /* or y */
c.get<Coord::z>(); /* or z */
getter

和 setter 旨在保护您的数据并提供封装。

例如,

它们允许您为获取和设置操作(例如写入日志(添加副作用,或者允许您在无效值以后导致可怕的问题之前尽早捕获无效值(例如,防止设置大于 n 的值(。

下面是一个简短而人为的二传手示例:

void set_x(int x)
{
    // prevent an invalid value for x
    if( x > 11 ) x = 11;
    // set x
    this.x = x;
    // log the operation
    log("user set x to {0}", x);    
}

假设您的c.set.x(5)示例没有使用一些古怪的预处理器宏,它将要求 Coord 类具有一个名为 set with 方法的成员变量

x()
y()
z()

这将需要与在Coord类中编写set_x()set_y()set_z()方法一样多的代码,但这些方法不属于类 Coord,而是属于另一个类,该类本身用作 Coord 的成员变量。这样做真的没有任何逻辑意义......x、y 和 z 值属于坐标,对它们的运算就是对坐标的运算。

此外,方法x() y()z()将不再遵循使方法动词的一般原则。任何使用这些方法阅读类的人都不知道函数 z(( 应该做什么!

它还会造成重构的噩梦:例如,如果将来出现一个业务需求,这意味着没有 Coords 的值可以大于 21 somone 维护您的代码将不得不更改作为 Coord 成员的类而不是 Coord 类本身。

使用 getter 和 setter 方法进行封装通常是一个非常好的主意,C++内联的好处甚至可以不增加运行时开销。但要坚持"让一切尽可能简单,但不要更简单"的原则。换句话说,get_x((和set_x((是广泛理解的,有用的,易于重构的,方便的,自我记录的和高性能的。其他方法可能不那么重要。

首先:您对c.set.x的使用不起作用,因为您会在对象c上调用公共元素set,并且set具有公共元素x

我发现这两个类都缺乏干净的代码标准和通常的getter和setter风格 - 即使没有指定任何语言。

一种常用的方法是创建以下内容:

class Coord
{
  double x;
  double y;
  double z;
public:
  Coord() { 
    x = 0;
    y = 0;
    z = 0; 
  }
  Coord(double x, double y, double z)
  {
    this.x = x;
    this.y = y;
    this.z = z;
  }
  void setX(double x) { this.x = x; }
  void setY(double y) { this.y = y; }
  void setZ(double z) { this.z = z; }
  double getX() { return x; }
  double getY() { return y; }
  double getZ() { return z; }
};

尽管有些人更喜欢使用 m_x 作为 setter 变量参数或任何其他约定。

无论如何,每个人都会直接理解你的代码。能够设置和获取 x、y、z 的坐标值,如果有人执行以下操作,它看起来会非常标准的默认行为:

Coord P(10, 15, 20);
std::cout << P.getX() << " " << P.getY() << std::endl;
P.setX(-10);
P.setZ(40);

你已经得到了一些很好的答案,但如果你真的想要一个具有更少 setter 和 getter 的语法,你也可以使用枚举。客户端语法可能会变得有点笨拙和笨拙,但这当然取决于您要查找的内容!

#include <iostream>
class Coord {
public:
    enum Axis {
        X = 0,
        Y,
        Z,
        NUM_AXES
    };
    // Using extended initializer lists (C++11)
    Coord() : axes_{0, 0, 0} {}
    Coord(double x, double y, double z) : axes_{x, y, z} {}
    void set(Axis a, double d) {
        axes_[a] = d;
    }
    double get(Axis a) const {
        return axes_[a];
    }
private:
    double axes_[NUM_AXES];
    // Copy constructor and assgn. operator included for good measure
    Coord(const Coord &);
    void operator=(const Coord &);  
};
int main()
{
    Coord c(1, 2, 3);
    std::cout << "X: " << c.get(Coord::X) << std::endl;
    std::cout << "Y: " << c.get(Coord::Y) << std::endl;
    std::cout << "Z: " << c.get(Coord::Z) << std::endl;
    c.set(Coord::Y, 4);
    std::cout << "Y: " << c.get(Coord::Y) << std::endl;
    return 0;
}

这个奇怪的代码完全按照你的要求去做 - 只是C++有趣。别这样!

#include <iostream>
using namespace std;
class Coord {
    double x;
    double y;
    double z;
public:
    class Setter {
    public:
        Setter(Coord& coord) : c(coord) {}
        void x(double value) { c.x = value; }
        void y(double value) { c.y = value; }
        void z(double value) { c.z = value; }
        void operator()(double x, double y, double z) { c.x = x; c.y = y; c.z = z; }
    private:
        Coord& c;
    };
    class Getter {
    public:
        Getter(Coord& coord) : c(coord) {}
        double x() { return c.x; }
        double y() { return c.y; }
        double z() { return c.z; }
    private:
        Coord& c;
    };
    Setter set;
    Getter get;
    Coord() :  set(*this), get(*this) { x = y = z = 0;  }
    friend class Setter;
};
int main()
{
    Coord c;
    cout << c.get.x() << " " << c.get.y() << " " << c.get.z() << endl;
    c.set.x(1);
    c.set.y(2);
    c.set.z(3);
    cout << c.get.x() << " " << c.get.y() << " " << c.get.z() << endl;
    c.set(5, 6, 7);
    cout << c.get.x() << " " << c.get.y() << " " << c.get.z() << endl;
    return 0;
}

输出:

0 0 0
1 2 3
5 6 7

如果验证不是问题,则公开这一点或多或少的标准方法是从非 const 访问器返回可变引用,并从 const 访问器返回值。 这允许您将接口与存储分开,同时不会使语法太重。

private:
    double m_x, m_y, m_z;
public:
    double & x() { return m_x; }
    double & y() { return m_y; }
    double & z() { return m_z; }
    double x() const { return m_x; }
    double y() const { return m_y; }
    double z() const { return m_z; }

这将允许c.x()获取 x 坐标的值,无论Coord对象是否是 const,并允许使用语法c.x() = value设置值。


为了完整起见,您可以使用以下代码获得所需的语法,但我强烈建议不要这样做。 它是大量额外的代码,没有提供真正的好处,并且创建了一种不常见的语法,大多数程序员不会发现它直观。

该技术创建两个嵌套类getterssetters,并将它们的实例公开为Coord的公共成员。

这是作为如何实现您要求的结果的示例提供的,但我不建议使用这种方法。

class Coord
{
private:
    double x, y, z;
public:
    Coord();
    Coord(double, double, double);
    class setters {
        friend class Coord;
    private:
        explicit setters(Coord &);
    public:
        setters(setters const &) = delete;
        setters & operator=(setters const &) = delete;
        void x(double) const;
        void y(double) const;
        void z(double) const;
    private:
        Coord & coord;
    };
    friend class setters;
    class getters {
        friend class Coord;
    private:
        explicit getters(Coord const &);
    public:
        getters(getters const &) = delete;
        getters & operator=(getters const &) = delete;
        double x() const;
        double y() const;
        double z() const;
    private:
        Coord const & coord;
    };
    friend class getters;
    setters const set;
    getters const get;
};
Coord::Coord() : x(0), y(0), z(0), set(*this), get(*this) { }
Coord::Coord(double px, double py, double pz) : x(px), y(py), z(pz), set(*this), get(*this) { }
Coord::setters::setters(Coord & c) : coord(c) { }
void Coord::setters::x(double px) const {
    coord.x = px;
}
void Coord::setters::y(double py) const {
    coord.y = py;
}
void Coord::setters::z(double pz) const {
    coord.z = pz;
}
Coord::getters::getters(Coord const & c) : coord(c) { }
double Coord::getters::x() const {
    return coord.x;
}
double Coord::getters::y() const {
    return coord.y;
}
double Coord::getters::z() const {
    return coord.z;
}

(演示(

实际上,该功能可以根据您的要求减少,如下所示,

class Coord
{
     double x;
     double y;
     double z;
   public:
      Coord() { 
            x = 0;
            y = 0;
            z = 0; 
      }
      void GetValues(double* x=NULL, double* y=NULL, double* z=NULL);
      void SetValues(double x=0, double y=0, double z=0)
      /* You can use constructors like below to set value at the creation of object*/
      Coord(double x, double y, double z)
      {
         this.x = x;
         this.y = y;
         this.z = z;
      }
     /*You can set the values of x, y & z in a single function as follows. It can be used at any time without restriction */
      void SetValues(double x, double y, double z)
      {
         if(x > 0) //It is optional to use condition so that you can update any one variable aloen by sending other two as ZERO
         {
            this.x = x;
         }
         if(y > 0)
         {
            this.y = y;
         }
         if(z > 0)
         {
            this.z = z;
         }
      }
      /*You can Get the values of x, y & z in a single function as follows. Pass By Reference id the concept you need */
      void GetValues(double* x, double* y, double* z)
      {
         if(x != NULL) //It x is not null. 
         {
            x = this.x;
         }
         if(y != NULL)
         {
            y = this.y;
         }
         if(z != NULL)
         {
            z= this.z;
         }
      }

};

通话时,您可以像下面这样打电话,

SetValues(10, 20, 0); //To set x and y values alone.
double x1 = 0;double y1 = 0;double z1 = 0;
GetValues(&x1, &y1, &z1)//It will return the values x1 y1 and z1 as 10, 20 & 0

你不能完全按照自己的意愿去做。

c.set.x(5); /* only set x */

c.set子表达式是从c中检索字段set(除非set#define -D 宏,但那会很愚蠢(。