我创建了一个新的类分数,因为输入分子和分母是常识。我的老人建议我私下宣布分子和分母。但是,如果它们可以被改变并且无论如何都会发生数据滥用,那么我为什么要将它们声明为私有?
我的分数类代码(如果你想引用我的代码):
#ifndef FRACTION_H
#define FRACTION_H
#endif // FRACTION_H
#include<string>
#include<iostream>
using namespace std;
class fraction{
int numer, denom;
void change_to_simplest_form();
void get_value_in_string();
public:
fraction(int,int);
fraction(){numer =0; denom =0;}
void display();
int get_num(){return numer;}
int get_denom(){return denom;}
float get_float_num(){return float(numer);}
float get_float_denom(){return float(denom);}
void get_it_all_done();
inline bool operator<(fraction &rhs){if(float(this->get_float_num()/this->get_float_denom())<float(rhs.get_float_num()/rhs.get_float_denom()))return true; else return false;}
inline bool operator>(fraction &rhs){if(float(this->get_float_num()/this->get_float_denom())>float(rhs.get_float_num()/rhs.get_float_denom()))return true; else return false;}
inline bool operator<=(fraction &rhs){if(float(this->get_float_num()/this->get_float_denom())<=float(rhs.get_float_num()/rhs.get_float_denom()))return true; else return false;}
inline bool operator>=(fraction &rhs){if(float(this->get_float_num()/this->get_float_denom())>=float(rhs.get_float_num()/rhs.get_float_denom()))return true; else return false;}
inline bool operator==(fraction &rhs){if(float(this->get_float_num()/this->get_float_denom())==float(rhs.get_float_num()/rhs.get_float_denom()))return true; else return false;}
inline void operator++(int){numer+=denom;}
inline void operator+=(int a){numer+=(denom*a);}
inline void operator--(int){numer-=denom;}
inline void operator-=(int a){numer-=(denom*a);}
inline void operator=(string a){bool denom_not_one = true;int i =0;numer=0;denom=0;for(i =0;a[i]!='/';++i){if(a[i]=='\0'){denom=1;denom_not_one=false;break;}if(int(a[i])>=48 && int(a[i])<=57){numer*=10;if(a[i]!='0'){numer+=(int(a[i]-48));}}}for(;a[i]!='\0' && denom_not_one;++i){if(int(a[i])>=48 && int(a[i])<=57){denom*=10;if(a[i]!='0'){denom+=(int(a[i]-48));}}}}
inline void operator=(fraction &rhs){this->numer=rhs.get_num();this->denom=rhs.get_denom();}
inline fraction operator*(fraction &rhs){fraction tmp(this->numer*rhs.get_num(),this->denom*rhs.get_denom()); return tmp;}
inline void operator*=(fraction &rhs){this->numer*=rhs.get_num();this->denom*=rhs.get_denom();}
inline void operator/=(fraction &rhs){this->numer*=rhs.get_denom();this->denom*=rhs.get_num();}
};
void fraction:: get_it_all_done(){
change_to_simplest_form();
}
fraction::fraction(int a, int b){
denom = b;
numer =a;
// display();
}
void fraction::change_to_simplest_form(){
int divisor = 1;
bool islessthanzero = false;
if(numer<0){
numer*=(-1);
islessthanzero = true;
}
while(divisor <= numer && divisor <= denom){
if(numer%divisor==0){
if(denom%divisor==0){
numer/=divisor;
denom/=divisor;
}
}
divisor++;
}
if(islessthanzero){numer*=(-1);}
}
void fraction::display(){
change_to_simplest_form();
cout << "(" << numer << "/" << denom << ")" ;
}
std::ostream &operator<<(std::ostream &os,fraction &m){
m.get_it_all_done();
if(m.get_denom()!=1){
cout << "(" << m.get_num() << "/" << m.get_denom() << ")" ;
}
else cout << m.get_num();
return os;
}
答案 0 :(得分:7)
这是 私有数据 - 公共接口 的理念和原则,其中用于更改任何私有字段的公共方法具有适当且适当的防御来改变私有字段程序中不需要的值(即避免破坏任何invariants)。如果该字段只是public
那么就没有这样的防御措施。
答案 1 :(得分:1)
事实上,让成员private
和public
吸气剂和制定者打破封装,所以从设计的角度看它都是一样的。
唯一的优点是能够在getter和setter中设置断点,这可以在调试期间提供帮助。
答案 2 :(得分:1)
隔离更改(来自实现)。
对于您的情况,如果您想将分子和分母的类型从int
更改为float
,如果它们在没有getter的情况下公开,则所有客户端也需要更改其代码。如果它们与public getter私有,则客户端不需要更改,因为接口不会更改。它使您可以更灵活地更改实现细节。
答案 3 :(得分:1)
您可以将您的教授推荐给这个答案。
在许多情况下,如果一个类只是一个方便的离散值占位符,那么数据就适合作为接口。
此示例是std::pair
,其中包含公开数据成员first
和second
。修改一个而不引用另一个是完全合适的,因此用get_first()
和set_first()
混淆代码是没有意义的(注意自由函数auto& std::get<0>(t)
和{{1模仿这种行为,但由于某种特定原因 - 它们使通用编程变得更容易。)
当数据成员为auto& std::get<1>(t)
时,data-is-interface的另一个时间是合适的。通过const,它不能改变,因此不需要get / set(它也是线程安全的,但这是另一个故事)。
所以你可以将你的分数实现为:
const
并允许更改参数。
但实际上在分数中,分子和分母并不是独立的。例如,设置分母零应该可以提前捕获(通过抛出异常!)并且您可能希望尽早将分数标准化,这取决于您实施分数算术的策略。
所以这开始变得更明智了:
struct frac
{
double numerator, denominator;
};
另一种方法是将您的分数设计为不可变的,并要求任何更改都会产生副本。
在这种情况下,这是合适的:
struct frac
{
frac(double n, double d) : numerator(n), denominator(d) {}
frac& set_denominator(double d)
{
if (d == 0.0) { // probably should check the ratio of n to d here
// but this is a simplified example for exposition
throw std::invalid_argument(std::to_string(d));
}
denominator = d;
normalise();
return *this;
}
void normalise() {
// find gcd etc etc
}
private:
double numerator, denominator;
};
每种形式的情况根据您的要求而有所不同。在多处理中,通常优选具有不可变的离散副本。它是Haskell和D等语言的常态。
答案 4 :(得分:0)
这与Encapsulation有关。如果你只是使用一个基本的设置器来操作它,这似乎是浪费时间,因此你的问题是,如果你仍然可以修改它,为什么甚至将其设为私有。但是,随着您的代码变得更加复杂,您将拥有其他方法来根据业务逻辑修改私有变量。
以私人变量weeklyEmployeePay为例,您可以编写一个名为calculateWeeklyEmployeePay的公共方法(双小时,双倍费率)。然后,这个公共方法将根据方法中的逻辑和给定的参数更改私有变量。
封装是面向对象编程的关键组件。
答案 5 :(得分:0)
为了防止滥用数据。当您编写的方法提供对它们的唯一访问时,您的方法可以以防止私有变量获取错误值的方式编写。如果调用者试图设置一个不合适的值,你可以做各种事情,包括:
- 抛出异常
- 将值夹在有意义的范围内 - 显示警告。这取决于你。
因此,虽然来电者可以向公共方法发送不适当的价值,但你可以选择对此采取行动。