与构造函数一起封装

问题描述 投票:0回答:4

我希望我放置的 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;
}

我希望你能理解我的问题,因为我不知道还能用什么方式来表达它。

c++ constructor encapsulation
4个回答
2
投票

从函数来看:

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

看来您想在

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

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
提供初始化程序,所以使用类内初始化程序 (
= 0
)。该成员是
unsigned
并且只能取正值,但是当您想要检查
setMedals
的子类或调用者提供的值是否为负数时,必须对参数进行签名。


1
投票

tl;博士: 在构造函数内调用非虚函数通常没问题,尽管在处理较大的对象时我会注意这一点。 所以,像这样的事情应该做:

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); 
  }
};

至于扩展答案:

第一个答案,即使用 unsigned int 就足够了。 然而,人们可能想知道 getter 和 setter 的全部目的是什么,如果它们实际上是访问变量的传递,因此不存在真正的封装。

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

struct Athlete
{
  unsigned medals;
};

如果需要某种输入验证/处理,例如

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
变量本身应该是不同的类型,保持其自身的不变性。 如果有人决定采用这种方法,它可能看起来像这样:

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

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

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


0
投票

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

unsigned int Medals {0};

设置的函数为:

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

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.