C++ Getter/Setter (Alternatives?)

C++ Getter/Setter (Alternatives?)

本文关键字:Alternatives Setter Getter C++      更新时间:2023-10-16

好吧,几乎在我读到的地方,我都读到getters/setters是"邪恶的"。现在,作为一个经常在 PHP/C# 中使用 getter/setter 的程序员,我看不出它们是如何活着的。我读过它们会破坏封装等,但是,这里有一个简单的例子。

class Armor{
  int armorValue;
public:
  Armor();
  Armor(int); //int here represents armor value
  int GetArmorValue();
  void SetArmorValue(int);
};

现在,让我们说getters和setter是"邪恶的"。初始化后应该如何更改成员变量。

例:

Armor arm=Armor(128); //armor with 128 armor value
//for some reason I would like to change this armor value
arm.SetArmorValue(55); //if i do not use getters / setters how is this possible?

假设上述内容不行,无论出于何种原因。如果我的游戏将护甲值限制在 1 到 500 之间,该怎么办?(任何盔甲都不能有超过 500 件盔甲或少于 1 件盔甲的一块)。

现在我的实现变成了

void Armor::SetArmor(int tArmValue){
      if (tArmValue>=1 && tArmValue<=500)
         armorValue=tArmValue;
      else
         armorValue=1;
}

那么,在不使用 getter/setter 的情况下,我该如何施加此限制呢?否则,如何在不使用 getter/setter 的情况下修改属性?在案例 1 中,armorValue 应该只是一个公共成员变量,而在案例 2 中是否应该使用获取者/设置者?

好奇。谢谢伙计们

你误解了什么。不使用 getter/setter 会破坏封装并暴露实现细节,对于邪恶的某些定义,可以被认为是"邪恶的"。

我想从某种意义上说,它们可以被认为是邪恶的,如果没有适当的 IDE/编辑器支持,它们在C++中编写起来有点乏味......

C++的一个缺陷是创建非常量引用getter,这也允许修改。这与返回指向内部数据的指针相同,并且会锁定内部实现的那部分,并且实际上并不比公开字段更好。

编辑:根据评论和其他答案,您听到的可能是指始终为每个字段创建非私有getter和setter。但我也不会称之为邪恶,只是愚蠢;-)

稍微相反:是的,获取者和设置者(又名访问器和突变者)大多邪恶的。

IMO,这里的邪恶不是来自"打破封装",而是简单地将变量定义为一种类型(例如,int),而实际上它根本不是那种类型。看看你的例子,你称 Armor 为int,但事实并非如此。虽然它无疑是一个整数,但它肯定是一个int,它(除其他外)定义了一个范围。虽然类型是整数,但它根本不打算支持与int相同的范围。如果希望Armor属于类型 integer from 1 to 500 ,请定义一个类型来直接表示该类型,并将Armor定义为该类型的实例。在这种情况下,由于要强制实施的不变量被定义为类型本身的一部分,因此无需在其上附加资源库来尝试强制执行它。

template <class T, class less=std::less<T> >
class bounded {
    const T lower_, upper_;
    T val_;
    bool check(T const &value) {
        return less()(value, lower_) || less()(upper_, value);
    }
    void assign(T const &value) {
        if (check(value))
            throw std::domain_error("Out of Range");
        val_ = value;
    }
public:
    bounded(T const &lower, T const &upper) 
        : lower_(lower), upper_(upper) {}
    bounded(bounded const &init) 
        : lower_(init.lower), upper_(init.upper), val_(init.val_)
    { }
    bounded &operator=(T const &v) { assign(v);  return *this; }
    operator T() const { return val_; }
    friend std::istream &operator>>(std::istream &is, bounded &b) {
        T temp;
        is >> temp;
        if (b.check(temp))
            is.setstate(std::ios::failbit);
        else
            b.val_ = temp;
        return is;
    }
};

有了这个,定义一些射程为 1..500 的装甲变得完全微不足道:

bounded<int> armor(1, 500);

根据具体情况,您可能更愿意定义(例如)saturating类型,其中尝试分配超出范围的值是可以的,实际分配的值只是范围内最接近的值。

saturating<int> armor(1, 500);
armor = 1000;
std::cout << armor;  // prints "500"

当然,我上面给出的也有点简陋。对于您的装甲类型,支持-=(可能+=)可能会很方便,因此攻击最终会像x.armor -= 10;一样。

底线:getter 和 setter 的主要问题(或至少是"一个")是,当您真的想要一些碰巧在几个方面相似的其他类型的变量时,它们通常指向您已将变量定义为一种类型。

现在,确实有些语言(例如Java)无法为程序员提供编写此类代码所需的工具。在这里,我相信您使用C++标签来表明您确实想写C++。C++确实为您提供了必要的工具,并且(至少IMO)您的代码会更好地利用它提供的工具,因此您的类型强制执行所需的语义约束,同时仍然使用干净,自然,可读的语法。

简而言之:他们不是邪恶的。

只要他们不泄露内部表示,他们就没有错。我认为这里没有问题。

对 get/set 函数的一个常见批评是,客户端代码可能会滥用它们来执行逻辑上应该封装在类中的操作。 例如,假设一个客户想要"抛光"他们的盔甲,并决定效果是将"价值"增加 20,所以他们做了他们的小事情并很高兴。 然后其他地方的其他客户端代码决定生锈的装甲应该将值降低 30,他们尽自己的一份力量。 同时,客户端代码中的其他十几个地方也允许对装甲进行抛光和生锈效果 - 以及"加固"和"开裂",并直接实现它们。 对此没有中央控制...装甲类的维护者没有能力做这样的事情:

  • 每件装甲最多应用一次锈蚀、抛光、加固和裂纹效果

  • 调整添加到或减去值的数字以获得特定的逻辑效果

  • 决定新的"皮革"装甲类型不会生锈,并忽略客户试图这样做

另一方面,如果第一个想要使 armour 生锈的客户端无法通过界面做到这一点,他们会去找 armour 类的维护者并说"嘿,给我一个函数来做这件事",那么其他人可以开始使用逻辑级别的"rust"操作,如果以后做我上面描述的那种事情变得有用,它们可以在 armour 类中轻松集中地实现(例如,通过一个单独的布尔值来表示装甲是否生锈,或者一个单独的变量记录生锈效应)。

因此,get/set 函数的问题在于它们挫败了逻辑功能 API 的自然演变,而是在整个客户端代码中分发逻辑,导致极端情况下导致无法维护的混乱。

你的getter/setter看起来不错。

getter/setter 的替代方法是将成员变量公开。更准确地说,将变量分组到没有成员函数的结构中。并在你的class

无论

何时,Getters都是邪恶的:

  1. 它们直接访问类的数据成员
  2. 每次向类添加数据时都必须添加新的 getter 时
  3. 每个吸气器的数据行为都不同

因此,好的获取者会做到以下几点:

  1. 他们将请求转发到其他对象或从多个位置收集数据
  2. 您只需使用一个 getter 即可获取大量数据
  3. 您获取的所有数据的处理方式相同

另一方面,二传者总是邪恶的。

否则我将如何在不使用 getter/setter 的情况下施加此限制?否则,如何在不使用 getter/setter 的情况下修改属性?

您可以检查从变量中读取的内容,如果其值超出范围,请使用预定义的值(如果可能)。

您还可以诉诸肮脏的黑客,例如保护变量下方的内存不被写入,捕获写入尝试以及禁止/忽略具有无效值的尝试。这将很难实现,而且执行起来很昂贵。不过,它可能对调试很有用。