与构造函数一起封装

Encapsulation along with constructors

我希望我设置为 privateint Medals 不能有负值,但我不知道如何与构造函数一起实现该封装。我这样做是为了让每个运动员类型都继承 Athlete 构造函数,但我不知道在哪里调用 setMedals 函数让它工作。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

class Athlete {
private:
    int Medals;
public:
    string Name;
    void setMedals(int newMedals) {
        if (newMedals >= 0)
            Medals = newMedals;
    }
    int getMedals() const{
        return Medals;
    }

    virtual string getDescription() = 0;
    Athlete(string _Name, int _Medals) : Name(_Name), Medals(_Medals) {}
};

class Footballer : public Athlete {
public:
    string getDescription() {
        return "Footballer ";
    }
    Footballer(string Name, int Medals) :  Athlete(Name, Medals) {}
};

class Basketballer : public Athlete {
public:
    string getDescription() {
        return "Basketballer ";
    }
    Basketballer(string Name, int Medals) : Athlete(Name, Medals) {}

};

ostream& operator <<(ostream& output, vector<Athlete*> athletes) {
    for (int i = 0; i < athletes.size(); i++) {
        output << athletes[i]->getDescription() << " " << athletes[i]->Name << ": " << athletes[i]->getMedals() << " Medals" << endl;
    }
    return output;
}

void printAthletes(vector<Athlete*> athletes) {
    sort(athletes.begin(), athletes.end(), [](Athlete* a, Athlete* b) {
        return a->getMedals() > b->getMedals(); });

        cout << athletes;
}

int main() {    
    Footballer Andrew("Andrew", 3), Jack("Jack", 4);
    Basketballer David("David", 5), Rob("Rob", 1);
    vector<Athlete*> Athlete = { &Andrew, &Jack, &David, &Rob };
    printAthletes(Athlete);

    
    return 0;
}

我希望你能理解我的问题,因为我不知道如何表达它。

您可以使用无符号值来确保变量不会有负值。

unsigned int Medals {0};

设置函数为:

void setMedals(unsigned int newMedals)
{
    Medals = newMedals
}

我认为您应该在构造函数中将奖牌值默认为 0,这样如果它是负数,就不会分配给您的 属性。 然后在你的构造方法中,你可以调用你的“setMedals”方法。

希望对您有所帮助。

来自函数:

void setMedals(int newMedals) {
    if (newMedals >= 0)
        Medals = newMedals;
}

看来您想将 Medals 的值设置为正值,否则什么也不做。为此,您首先必须为 Medals 提供不同的初始化程序。当提供给构造函数的值错误时,它将采用一些值。请注意,您可以使成员 unsigned 无论如何它应该只存储正值。最终,您可以在 Athlete 构造函数中调用 setMedals。通常最好在构造函数中初始化成员而不是赋值。但是,正如你想要的初始化+可选赋值,两者都做是可以的:

class Athlete {
private:
    unsigned Medals = 0;   // <-  initializer here
public:
    string Name;
    void setMedals(int newMedals) {
        if (newMedals >= 0)
            Medals = newMedals;
    }
    int getMedals() const{
        return Medals;
    }

    virtual string getDescription() = 0;
    Athlete(string _Name, int _Medals) : Name(_Name) { // <- no initializer here
        setMedals(_Medals);  // <- call the setter
    }
};

因为构造函数没有为 Medals 提供初始值设定项,所以使用了 in-class 初始值设定项 (= 0)。该成员是 unsigned 并且只能取正值,但是由于您要检查 setMedals 的子类或调用者提供的值是否为负值,因此必须对参数进行签名。

tl;博士: 在构造函数中调用 non-virtual 函数通常没问题,尽管在处理较大的对象时我会注意它。 所以,应该这样做:

class Athlete
{
private:
  unsigned medals{0};
public:
  string name;
  void setMedals(int m) //or just use unsigned...
  {
    if (m >= 0)
      medals = m;
  }
  unsigned getMedals() const{return medals;}

  virtual string getDescription() = 0; //should be const maybe?
  Athlete(string n, int m) : name(move(n))
  {
    setMedals(m); 
  }
};

至于扩展答案: 首先,一个简短的免责声明。我想知道我是否应该在这里回答这个问题,或者将它标记给版主以将主题主题移动到软件工程或其他类似的 SE 站点,因为讨论很可能会转向一般的“架构”。 如果版主、用户或 OP him/herself 喜欢,请这样做。

话虽如此,正题:第一个答案,即使用unsigned int就可以了。 然而,人们可能想知道 getters 和 setters 的全部目的是什么,如果它们实际上是 pass-through 来访问变量,那么就没有真正的封装。

出于这个原因,人们可以简单地考虑这样的事情(为简洁起见简化了界面):

struct Athlete
{
  unsigned medals;
};

如果需要某种输入 validation/processing,例如medals不能超过10,可以考虑用setter和getter,例如

class Athlete
{
public:
  explicit Athlete(unsigned m)
  : medals {clamp(m, 0, 10)}
  //or throw from constructor
  //depedns what one wants really
  {}
  unsigned getMedals() const { return medals; }
  void setMedals(unsigned m) { medals = clamp(m, 0, 10); }
  //again, you may throw or do anything else
  //clamping is just an example
private:
  unsigned medals;
};

但是,这里出现了一个关于对象责任的问题。 也许 Athlete 不应该关心奖牌的数量(或值代表的任何值),但 Medals 变量 iself 应该是保持其自身不变性的不同类型。 如果决定采用这种方法,它可能看起来像这样:

template <typename T, T LO, T HI>
class LimitedInt
{
//all the needed operations
};

struct Athlete
{
  using Medals = LimitedInt<unsigned, 0, 10>;
  Medals medals;
};

不幸的是,这里没有简单的答案哪个更好或更差,这取决于多种因素,更不用说代码风格和使用的框架是其中之一,例如QT 按照惯例广泛使用 getters 和 setters。