(C++)如果我在类中声明了一些私有的东西,但它可以通过类的公共方法进行更改,那么我为什么要声明它为私有的呢

(C++ )If I declare something private in a class but it can be changed via public methods of class, then why should I declare it private?

本文关键字:声明 方法 为什么 如果 C++ 可以通过      更新时间:2023-10-16

我创建了一个新的类分数,因为输入分子和分母是常识。学长建议我私下申报分子和分母。但是,如果它们可以更改,并且无论如何都可能发生数据滥用,那么我为什么要宣布它们为私有?

我的分数类代码(如果你想参考我的代码):

#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]==''){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]!='' && 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;
}

这是私有数据-公共接口的思想和原理,用于更改任何私有字段的公共方法具有适当的防御措施,可以防止将程序中的私有字段更改为不需要的值(即避免破坏任何不变量)。如果场地只是public,那么就不会有这样的防御。

事实上,让成员private和拥有public getter和setter会破坏封装,所以从设计的角度来看,这一切都是一样的。

唯一的优点是能够在getter和setter中设置断点,这在调试过程中会有所帮助。

隔离更改(与实现)。

对于您的情况,如果您想将分子和分母的类型从int更改为float,如果它们是公共的而没有getter,那么所有客户端也需要更改它们的代码。如果它们与公共getter是私有的,则客户端不需要更改,因为接口不会更改。它使您可以更灵活地更改实现细节。

你可以让你的教授参考这个答案。

在许多情况下,如果类只是离散值的方便占位符,那么数据作为接口是合适的。

这方面的示例是具有公共数据成员firstsecondstd::pair。修改其中一个而不参考另一个是完全合适的,因此用get_first()set_first()混淆代码是没有意义的(请注意,自由函数auto& std::get<0>(t)auto& std::get<1>(t)模仿了这种行为,但有一个特定的原因-它们使通用编程更容易)。

另一个时间数据是接口更合适的是当数据成员是const时。由于是const,它不能改变,所以不需要get/set的想法(它也是线程安全的,但那是另一回事)。

这样你就可以实现你的分数:

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

另一种方法是将分数设计为不可变的,并要求任何更改都产生一个副本。

在这种情况下,这是合适的:

struct frac
{
  frac(double n, double d) : numerator(n), denominator(d) {}
  frac replace_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));
    }
    return normalised_frac(numerator, d);
  }
  const double numerator, denominator;
};
frac normalised_frac(double n, double d)
{
  // normalisation operations on n and d
  return frac { n, d };
}

根据您的要求,每种形式的箱子的强度都会有所不同。在多处理中,通常首选具有不可变的离散副本。这是Haskell和D.等语言的规范

这与封装有关。如果你只使用一个基本的setter来操作它,这似乎是在浪费时间,因此你的问题是,如果你仍然可以修改它,为什么还要将其设为私有。然而,随着你的代码变得越来越复杂,你将有其他方法根据业务逻辑修改私有变量。

例如,以一个私有变量weeklyEmployeePay为例,您可以编写一个名为calculateWeeklyEmperoyeePay(双倍小时,双倍费率)的公共方法。然后,这个公共方法将根据方法中的逻辑和给定的参数来更改私有变量。

封装是面向对象编程的关键组成部分。

为了防止数据滥用。当您编写的方法提供了对它们的唯一访问权限时,可以以防止私有变量获得坏值的方式编写方法。如果调用方试图设置一个不合适的值,您可以执行各种操作,包括:
-引发异常
-将值限制在有意义的范围内
–显示警报。这取决于你

因此,尽管调用者可以向公共方法发送不合适的值,但您可以选择对此进行处理。