从 C++ 中的类访问私有变量

Posted

技术标签:

【中文标题】从 C++ 中的类访问私有变量【英文标题】:Access private variables from class in C++ 【发布时间】:2016-05-02 09:02:29 【问题描述】:

我正在做一个项目,我想在一个类中声明私有变量,因为我在很多地方都读到这比将它们声明为 public 更好,但是我如何在 main 中访问它们?我应该使用什么样的功能来使它们可以访问?我想通过一个不是像我所做的那样来自 main 的函数来解决系统问题。 到目前为止,这就是我的代码,

#include <iostream>
#include <limits>

using namespace std;

class Equation

    private:
        int a1, a2, b1, b2, c1, c2;

    public:
;

int main() 

    int a, b, c, d, e, f;
    cout << "\n" << endl;
    cout << "                        **** Hello ****               \n\n" << endl;
    cout << "This is a program to solve a system of equation" << endl;
    cout << "Equations will look like a1*x+b1*y=c1" << endl;
    cout << "and  a2*x+b2*y=c2\n" << endl;
    cout << "Enter the values for the first equation \n" << endl;

    while ((cout << "Enter the value of a1 :\n") && !(cin >> a)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    while ((cout << "Enter the value of a2 :\n") && !(cin >> b)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    while ((cout << "Enter the value of b1 :\n") && !(cin >> c)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    while ((cout << "Enter the value of b2 :\n") && !(cin >> d)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    while ((cout << "Enter the value of c1 :\n") && !(cin >> e)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    while ((cout << "Enter the value of c2 :\n") && !(cin >> f)) 
    
        cout << "Invalid input, please enter a number \n" << endl;
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    cout << "The first equation is : " << a << "x" <<
        "+" << c << "y" <<
        "=" << e << "\n" <<
        endl;

    cout << "The second equation is : " << b << "x" <<
        "+" << d << "y" <<
        "=" << f << "\n" <<
        endl;

    double x = ((c * e) - (b * f)) / ((a * e) - (b * d));
    double y = ((a * f) - (c * d)) / ((a * e) - (b * d));

    cout << "The solution of the system is " <<
        "x = " << x << "\t" <<
        "y = " << y <<
        "\n\n" <<endl;
    cout << "                        **** Thank You ****                 " << endl;

    return 0;

【问题讨论】:

我在很多地方都读到过,这比将它们公开要好,但是我如何在 main 中访问它们 -- 你创建这些变量 private 的原因是您确实希望main 或任何其他外部实体访问这些变量。 @AndreyChernukha 你教他们如何在脚上开枪并没有帮助他们。 @BaummitAugen 好吧,她绝对是个菜鸟,她甚至不知道什么是 setter 和 getter。你真的认为她应该不知道他们的存在吗?她现在发现它们不好还为时过早,因为她不明白为什么。对她来说,现在熟悉他们就足够了。她明天不打算写 Windows。你不这么认为吗? @GermelindaMusliaj 你还没有完全理解写一个类意味着什么。你写的这门课应该做什么?我知道这不仅仅是设置成员变量。这门课有一些目的。您可以通过编写public 成员函数来传达该目的。如果是解方程,那么你写一个成员函数来设置变量。用户并不关心你的类的内部是什么样子,只要它正在做一些设置变量的事情。然后你写一个Solve() 函数来解方程。 @GermelindaMusliaj 如果您将类更改为使用 6 个值的数组而不是 6 个单独的变量,该怎么办?再说一次,对于我(您的班级的用户)来说,知道您在 Equation 班级中使用了数组是否有意义?我为什么要在乎?只要您提供一个将一些值作为参数的“set_variables”函数,这对用户来说就是最重要的。同样,如果在以后的版本中设置变量很复杂怎么办?同样,只要用户调用“set_variables”函数,用户的代码就不会改变。只有您的内部课程代码会更改。 【参考方案1】:

面向对象编程最强大的方面之一是具有封装性,即每个对象以自己的方式处理自己的数据。人们建议将变量声明为私有成员的原因是为了确保类始终处于有效状态。相信你的课程。赋予他们责任,有权处理自己的系统。

另一个要点是关注点分离。基本上,如果您要执行的操作需要一个对象来更改其变量,那么让对象处理该操作。实现这一点的方法是在类的公共部分声明一个函数,让您的客户(与您的类交互的任何代码)“请求”该类进行更改并让你的班级处理这种变化。这是信任你的课程的第二步(我喜欢称之为 Empowering your classes)。是否需要使用您的类来更改数据?让你的班级去做。不要让其他实体通过命令你的班级来欺负你的班级。让他们问,不要乞求。

抛开所有的玩笑不谈,GettersSetters 是这种方法的(可以说)令人作呕的副产品。从技术上讲,在您的班级中拥有这些符合面向对象编程的方案,因为您正在为您的客户提供一个接口来与您的数据进行交互。但是,它的工作方式与我们首先使用访问修饰符的原因背道而驰。这样的做法在计算机科学中通常被称为anti-patterns,这意味着它是一种变异的、危险的模式形式。我个人认为Getters 没有那么糟糕,但Setters 是各种令人讨厌的错误的根源。

RobClucas 的回答将是我要采取的方向。但不是通过构造函数发送数据,我还将输入读取功能带入EquationSolver 类。这种方法似乎更类似于您最初的计划,并且涉及的代码更改更少。

你的班级应该是这样的:

class EquationSolver 
public:
    void GetEquationParameters() 
        /* This is where your while(cin) blocks are
         You are reading information directly into this class' paramters
         while(cin>>a1)
         while(cin>>a2)
         etc..

        See how you're "asking" this class to read data from the user.
         */

        while ((cout << "Enter the value of a1 :\n")
            && !(cin >> a1)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        

        while ((cout << "Enter the value of a2 :\n")
            && !(cin >> a2)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        

        while ((cout << "Enter the value of b1 :\n")
            && !(cin >> b1)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        

        while ((cout << "Enter the value of b2 :\n")
            && !(cin >> b2)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        

        while ((cout << "Enter the value of c1 :\n")
            && !(cin >> c1)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        

        while ((cout << "Enter the value of c2 :\n")
            && !(cin >> c2)) 
            cout << "Invalid input, please enter a number \n" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        
    

    /*
     * Functions for your client to use to get the final data.
     * Former is the C++ style that returns both values as a pair.
     * Latter is the sweet sweet C style where you fill x and y inside the function.
     * Latter is less data transaction if you care about performance that much.
     */
    std::pair<double, double> SolveEquationSystem() const 
        double x = (b1*c1 - a2*c2) / (a1*c1 - a2*b2);
        double y = (a1*c2 - b1*b2) / (a1*c1 - a2*b2);

        return std::pair<double, double>(x, y);
    
    void SolveEquationSystem(double * x, double * y) const 
        *x = (b1*c1 - a2*c2) / (a1*c1 - a2*b2);
        *y = (a1*c2 - b1*b2) / (a1*c1 - a2*b2);
    

    /**
     * Human readable print
     */
    void PrintEquation() 
        cout << "The first equation is : " << a1 << "x" << "+" << b1 << "y" << "=" << c1 << "\n" << endl;
        cout << "The second equation is : " << a2 << "x" << "+" << b2 << "y" << "=" << c2 << "\n" << endl;
    
private:
    int a1, a2, b1, b2, c1, c2;
;

在主函数中,你是这样使用它的:

void main() 
    cout << "\n" << endl;
    cout << "                        **** Hello ****               \n\n" << endl;
    cout << "This is a program to solve a system of equation" << endl;
        cout << "Equations will look like a1*x+b1*y=c1" << endl;
    cout << "and  a2*x+b2*y=c2\n" << endl;
    cout << "Enter the values for the first equation \n" << endl;

    // Create solver
    EquationSolver solver;

    // Ask solver to get parameters from the user
    solver.GetEquationParameters();
    // Ask solver to print equation in human readable format
    solver.PrintEquation();

    double x, y;
    // Ask solver to do what it's supposed to do
    solver.SolveEquationSystem(&x, &y);

    cout << "The solution of the system is " << "x = " << x << "\t" << "y = " << y << "\n\n" << endl;
    cout << "                        **** Thank You ****                 " << endl;


看看我是如何与班级互动的。作为main 的客户,我要求 提供给我的一个类以执行操作。我要求它获取 它的 将要使用的参数,然后要求它使用 它的 数据来解决系统问题。这样,如果出现错误,则与课程无关;这就是我使用课程的方式。

旁注:尽量不要使用using namespace std。我知道学校教你使用它,但这只是你的老师懒惰。 using namespace x 将整个命名空间包含在头文件中,虽然对于小型项目来说没什么大不了的,但随着项目规模的扩大,它可能会导致一些严重的问题。

如果你真的想摆脱std::,比如coutendl,那么你可以使用using std::cout;using std::endl;而不是整个命名空间。不过对于非特定功能,我还是建议你使用std::(比如我用std::numeric_limits表示我不是所有者,我只是在使用它)。

【讨论】:

【参考方案2】:

您必须为您的类提供一些设置值的公共函数和返回这些值的其他函数(如果您想知道的话)。

` #include #include 使用命名空间标准; 类方程 私人的: 诠释_a1,_a2,_b1,_b2,_c1,_c2; 民众: Equation() // 你可以在这里设置变量的默认值 虚拟 ~Equation() void set_eq1(int a, int b, int c) _a1 = a; _b1 = b; _c1 = c; void set_eq2(int a, int b, int c) _a2 = a; _b2 = b; _c2 = c; //... int get_a1() 返回 _a1; int get_a2() 返回 _a2; //... ; 主函数() 整数a、b、c、d、e、f; 双 res1, res2; 方程 eq; cout > 一; cout > b; cout > c; eq.set_eq1(a, b, c); // 等等... res1 = eq.solve_1(); res2 = eq.solve_2(); 返回0; `

塞尔吉奥

【讨论】:

不好的建议。 1.您的代码甚至无法编译。 2. 不保护任何不变量的 getter 和 setter 没有任何用处,只会增加噪音。 3、为什么非多态类要加virtual析构函数? 4. 这段代码并没有真正解决任何问题,只是在main 中声明了所有变量。 must 让我胃痛。程序员没有编写面向对象程序。【参考方案3】:

正如 Paul 提到的,如果您在 Equation 类中将变量设为私有,那么您不希望 Equation 类以外的任何实体访问这些变量。

在这种情况下,你应该使用类的私有成员来求解x和y(因为类成员可以访问类的私有变量),并提供一个公共接口来访问系统求解的结果。

例如:

#include <utility>  // For std::pair

class EquationSolver 
public: 
  EquationSolver(int _a, int _b, int _c, int _d, int _e, int _f)
  : a(_a), b(_b), c(_c), d(_d), e(_e), f(_f), x(0.0), y(0.0) 

  // This is the interface for solving the equations via method a
  // And allows outside entities to get the result of solving the system
  std::pair<double, double> solveViaMethodA() 
    solveForXInternally();  // This solves for X via the private implementation below
    solveForYInternally();  // This solves for Y via the private implementation below

    // x and y have been updated internally, so they can be returned as the result of the solved system
    return std::make_pair(x, y);
  
private:
  int a, b, c, d, e, f;
  double x, y;

  // This is an implementation for solving for x 
  void solveForXInternally() 
    x = ((c*e)-(b*f))/ ((a*e)-(b*d));
  
  // This is an implementation for solving for y 
  void solveForYInternally() 
    y = ((a*f)-(c*d))/ ((a*e)-(b*d));
  
;

现在你可以使用这个类来解方程了:

int main() 
  // Create an instance of the EquationSolver class
  EquationSolver solver(1, 2, 3, 4, 5, 6);

  // Use the instance of the solver to solve the equations
  std::pair<double, double> solved_variables = solver. solveViaMethodA();

虽然此示例说明了使用类来存储求解方程所需的系数,但求解单个方程并不是必需的。更简洁的解决方案是使用单个函数,如下所示:

#include <utility>

std::pair<double, double> solveSystem(int a, int b, int c, int d, int e, int f) 
  double x = ((c*e)-(b*f))/ ((a*e)-(b*d));  
  double y = ((a*f)-(c*d))/ ((a*e)-(b*d));
  return std::make_pair(x, y);
;

int main() 
  std::pair<double, double> solved_variables = solveSystem(1, 2, 3, 4, 5, 6);

然而,使用类确实允许解决方案易于扩展。例如,在我提供的 EquationSolver 类中,如果您想以不同的方式求解方程,那么您可以实现额外的私有成员函数以在内部求解 x 和 y,并添加另一个公共成员函数以通过替代方法访问解决方案。

【讨论】:

【参考方案4】:

当然,理解如何与类的privates 交互意味着了解privateclass 以及所有这些的要点,概括为"encapsulation," 是OOP 提供的主要属性。

面向对象的编程是关于对象通过接口相互交互的。这些接口为用户提供更改对象状态的功能。例如,在 3D 上下文中,可以旋转立方体,可以检索其中一个顶点的坐标等。 为什么是接口?隐藏实现细节。内部变量(private 变量)不应该对对象的任何用户可见。 OTOH,有暴露的变量(标记为public),它们描述了我正在谈论的那个界面。 为了完整起见,我将提到protected,它与派生类共享其从属变量,但这与这个问题无关,IMO。 但是为什么要隐藏实现细节呢?使用户独立于对象的内部结构和状态。用户看不到任何更改,从而提高了可维护性、代码可读性和代码可重用性。

那么,如何进一步进行?您需要考虑如何使用Equation 类以及它的内部状态和暴露。一个方程由许多变量组成。它们应该是内部状态。它有两个方面(也许更多)。你需要某种方式来代表它们。方程运算:求解它们?执行等价变换?打印出来?这是你必须考虑的。

注意:其他人提到了 setter 和 getter。它们分别用于设置和获取对象的成员变量。这很糟糕,因为它们破坏了封装;它们直接改变对象的状态,违背了 OOP 的目的,除非尝试使可变成员变量显示为常量或使用自定义代码控制变量的修改。

【讨论】:

【参考方案5】:

一个非常不受欢迎的选择是使用 setter 和 getter。

class A 
  public:
    void set_life(int life)  life_ = life; 
    void set_greeting(std::string greeting)  greeting_ = greeting; 
    int get_life() const  return life_; 
    std::string get_greeting() const  return greeting_; 
  private:
    int life_;
    std::string greeting_;
;

这些将按以下方式使用:

int main() 
  A a;
  a.set_life(42); // My object's life_ variable is now 42.
  a.set_greeting("Hello, World!"); // My object's greeting_ variable is now "Hello, World!".

  std::cout << a.get_greeting() << " The meaning of life is " << a.get_life() << '\n';
  // "Hello World! The meaning of life is 42

这被认为是错误的,因为如果您将 getter 和 setter 作为每个私有变量的规则引入,您不妨将变量公开 - 无论如何它们都可以更改。

一个更好的做法是使用你的构造函数来设置你的变量并创建一个方法来做你需要的变量——程序员不需要知道它们(因此是私有的)。

class A 
  public:
    // Construct life_ with life and greeting_ with greeting
    A(int life, std::string greeting) : life_(life), greeting_(greeting) 
    void print_message() const  
        std::cout << greeting_ << " The meaning of life is " << life_ << '\n';
    
  private:
    int life_;
    std::string greeting_;
;   

现在你的 main 看起来像这样:

int main() 
  A a(42, "Hello, World!");
  a.print_message();

【讨论】:

@Cheersandhth.-Alf 我认为“使用构造函数”和“实现使用变量的方法”的想法很有用。 getter+setter 与公共数据成员相当不同,因为至少有一个明显的地方可以添加范围检查或其他验证。或者实现“onChange”类型的回调。 我同意 Ben 的观点,getter 和 setter 提供了一种非常精确的方法来控制对私有成员的访问,不知道为什么它们在 c++ 中不受欢迎,因为它们在 Java 和 C# 中很常见。 @DominicNewman 如果您提供公共方法来更改私有数据,那么您就有公共数据。

以上是关于从 C++ 中的类访问私有变量的主要内容,如果未能解决你的问题,请参考以下文章

从不同的类访问私有访问变量数据

从测试访问私有变量并完成覆盖功能

从 PHP 中的类外部调用私有方法和私有属性

在继承的类中使用私有变量 - Java

C++ 友元函数不起作用,在此上下文中为私有错误

ZeroC ICE源代码中的那些事 - 嵌套类和局部类