C++:获取器和设置器?

3

我正在尝试编写一些代码来为我正在编写的一个类中的以下数据创建ID号码、名字、姓氏、期中成绩和期末成绩的getter和setter。这些数据存储在一个文本文件中...

10601   ANDRES HYUN 88 91 94 94 89 84 94 84 89 87 89 91 
10611   THU ZECHER 83 79 89 87 88 88 86 81 84 80 89 81 
10622   BEVERLEE WAMPOLE 95 92 91 96 99 97 99 89 94 96 90 97 
10630   TRUMAN SOVIE 68 73 77 76 72 71 72 77 67 68 72 75 

ID是第一个数字,名字的第一个和最后一个是不言自明的,期中成绩是倒数第二个数字,最后一个数字是期末成绩。这是我第一次使用getter和setter,到目前为止我已经...

int getID()const{return ID;}
void setID(int i) {ID=i}
string getFname()const{return Fname;}
void setFname(string f) {Fname = f}
string getLname()const{return Lname;}
void setLname(string l) {Lname = l}
int getMidterm()const{return Midterm;}
void setMidterm(int m) {Midterm=m}
int getFinal()const{return Final;}
void setFinal(int fl) {Final=fl}

我是否完全正确?另外,我如何指定数据集中的第一个数字是ID,第一个字符串是名字,第二个字符串是姓氏,倒数第二个数字是期中成绩,最后一个数字是总成绩?


你的问题是关于如何从给定的文本文件中获取输入并将其存储到你的类中,使用那些setter/getter吗?设置这些setter/getter是必需的吗? - greatwolf
看起来不错,但我建议你将字符串通过常量引用传递给你的设置器,而不是按值传递(使用string const &代替string)。 - japreiss
看起来一切都很好。你可能想要对字符串使用const引用。 - Julien Guertault
1
我正试图编写一些代码来创建getter和setter... 停止吧,这已经是一个不好的想法了。 - Jerry Coffin
3个回答

5

如果你在询问如何编写getter和setter方法,是的,这是很多人的做法。为了编写针对复杂类型的“完美”访问器,你可以使用以下代码:

void setLname(const String& input) { Lname = input; }    //copies the string
void setLname(String&& input) { Lname = std::move(input); }    //moves the string
const String& getLname() const { return Lname; }    //allows to get a const String reference from a const object
String& getLname() { return Lname; }    //allows modifying access to Lname if the object is not const.

请注意,这里所说的不仅仅是人们通常谈论的两个函数,通常只会看到一个getter和一个setter。很少有人想写得比这更多。然而,这四个函数中的每一个都有稍微不同的工作,可能建议添加。
但是,过多的访问器是一种代码异味:
  1. 它们在代码中散布着不提供任何抽象的函数。
  2. 当您同时拥有getter和setter时,您实际上可以无限制地访问该数据成员;因此,您可以编写public:String Lname
  3. 大量的访问器是您正在对数据进行建模而不是行为的明确标志。在这些情况下,您应该重新考虑设计,或者诚实并继续使用公共成员,或者如果所有成员都是公共的,则使用struct
请注意,我在这里谈论的是纯访问器,而不是那些实际上做一些有意义的事情的访问器,例如检查值范围,相应地更新其他成员等等。而这些纯粹的访问器是您应该避免的。
因此,如果我是您,我会问自己:我需要从我的类中获得什么行为?我真的需要在其他代码中谈论所有它的数据成员,还是可以找到合理的抽象来使访问器无用?我是否可能正在考虑建模错误的对象类别?还是我真的只想要一个可以在任何地方使用和修改的数据容器?

前两个访问器也可以写成 void setLname(String input) { Lname = std::move(input); }。请注意,如果无法应用移动省略(例如 x.setLname(std::move(some_lvalue))),则可能会触发一个以上的移动。 - dyp

1

看起来不错。以下是一些注意事项:

  • string作为const string&传递,以避免创建无用的副本。
  • 增加更多的空格可以提高可读性。
  • 不要使用缩写。 getFirstName()getFname()更易读。

至于你的第二个问题,你可以使用C++中许多可用的扫描/流工具,如scanfsscanffscanfstringstreamiostreamfstream等。

std::ifstream inFile("database.txt");
std::string line;
while (std::getline(inFile, line)) // for each line
{
    std::stringstream ss;
    ss << line;

    int id;
    ss >> id;

    std::string firstName;
    ss >> firstName;

    // etc
}

没问题,Bob。ifstream是“输入文件流”,用于读取文件。getlineinFile中读取一行并将其存储在line中,并在文件为空之前返回true。C++中的流允许您使用>><<运算符从不同类型中读取和写入数据,因此我通过将line写入流ss来将其转换为stringstream。现在我可以自由地从流中提取数据。id << ss从流中提取第一个整数并将其存储在id中。firstName << ss从流中提取一个单词并将其存储在firstName中。 - Nicolas Louis Guillemot
1
上述的调用方式没有重载operator <<。从std::stringstream(或任何其他STL流)读取数据使用operator >>,向流中写入数据使用operator <<。对于这两种运算符类型,第一个参数是流对象,第二个参数是要读取/写入的值。所以在你的例子中,读取一个int的操作是通过ss >> id;实现的。 - Ionut
你说得完全正确,我总是搞错了。谢谢,我已经编辑了我的帖子以反映你所说的内容。 - Nicolas Louis Guillemot

-1

第一部分:
这正是getter和setter方法应该看起来的样子(至少在Java中)。

第二部分:
要设置类的字段,您仍然需要手动解析输入。 我建议研究C++ iostreams,特别是用于输入流的>>运算符


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接