Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Accessing class members



  • I'm new to c++ and OOP... In mainwindow.h, I declare the following class :

    class Measurements{
    public:
    QString Sample;
    float CIE_L;
    float CIE_a;
    float CIE_b;
    };

    In mainwindow.cpp, I instantiate of object of this class:

    MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) // Constructor
    , ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    Measurements Measures;
    Measures.Sample="Yellow";
    Measures.CIE_L=87.45;
    Measures.CIE_a=-0.87;
    Measures.CIE_b=98.32;
    }

    I would like to access the members of the class from within a slot (hope that's the right name for this concept, I'm new to QT) :

    void MainWindow::on_btnGetDevice_clicked()
    {
    float DisplayValue = Measures.CIE_L;
    }

    But that does not "work". I confess, despite my research, I have a hard time understanding the correct way to go about accessing the object.

    Just to give you some quick background, I have a function which retrieves data from a device, and I would like to be able to display the data in a QTableWidget. I was considering the various data types available in c++ for doing this such as a structure but, sooner or later, I know I have to bite the bullet and start using "objects". Most classes and objects examples on the net are Console applications where classes are declared above the main() and objects are instantiate inside the main() function. There's never any "access" problems in this context. But in a multi-function applications, it's not obvious how the data interchange should / can take place? Apologize in advance for those who are offended by this type of newbie questions and think that I should go read a dozen c++ books...


  • Moderators

    Measures is a local variable inside your constructor. When the constructor is finished at } the variable goes out of scope and is destroyed. See this link for comprehensive information about variables scope in C++.

    What you need to do is change the scope of that variable from being a local function variable to a class member. this way it will live as long as your entire MainWindow object lives. So Instead of placing Measurements Measures; in the constructor put it in the MainWindow class declaration in the header file.



  • @RogerBreton said in Accessing class members:

    Apologize in advance for those who are offended by this type of newbie questions and think that I should go read a dozen c++ books...

    All of us have start with 0 knowledge, there is no need to read dozen of C++ books, but it is helpful to take time the learn some basic information about a language before starting programming.
    Some very basic things like:

    • variable scope / life cycle => in your code you have define a local variable which will be destroyed at function end for example.
    • class structure


  • @RogerBreton said in Accessing class members:

    that I should go read a dozen c++ books...

    You should read one ;-)

    You need to understand instances and lifetimes/scopes.

    float DisplayValue = Measures.CIE_L;
    

    Where is that Measures instance going to come from?

    I see you have Measurements Measures; in the MainWindow constructor. But that means that Measures instance goes out of scope at the end of the constructor. It's not going to be available in some other method.....

    Now, maybe you intended that Measurements Measures; statement to be in mainwindow.h, as a member variable of the MainWindow class...?



  • Thank you all for your prompt and patient replies.
    I understand that anything declared inside some scope "disappears" into oblivion as soon as it goes out of scope.

    So I moved my class declaration code inside the mainwindow class :

    class MainWindow : public QMainWindow
    {
    Q_OBJECT // Macro Meta Object Compiler
    public:
    class Measurements{
    QString Sample;
    float CIE_L;
    float CIE_a;
    float CIE_b;
    };

    First of all, does that mean I will end up with a 'sub-class'? Since I'm declaring a class within a class? It does not seem quite correct, intuitively, to me.

    Back in mainwindow.cpp, I can instantiate an object of this class in one of the mainwindow member functions this way :

    void MainWindow::on_btnGetDevice_clicked()
    {
    Measurements Mesures;
    Mesures.CIE_L = 87.45;
    }

    That's "progress" but I can't seem to be able to instantiate it at the Constructor level, so that it would be available to all mainwindow member functions? You see, if I try here :

    MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) // Constructor
    , ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    Measurements Mesures;
    Mesures.CIE_L=87.45;
    }

    Then, I can't access Mesures.CIE_L values from within any member functions, such as here :

    void MainWindow::on_btnGetDevice_clicked()
    {
    float test = Mesures.CIE_L;
    }

    This does not work, the compiler sees it as an 'undeclared identifier'?
    Sorry if I seem to make this more complicated that it is...


  • Moderators

    Sorry to say this but you've got it all wrong. No worries, we'll help you fix it.
    Let's start with your class declaration. We didn't say to put it inside MainWindow class declaration. We said to create a member of that class inside MainWindow declaration, so not this:

    class MainWindow : public QMainWindow
    {
       ...
       class Measurements{ ... };
    };
    

    but this:

    class Measurements{ ... };
    
    class MainWindow : public QMainWindow
    {
       ...
       Measurements Measures;
    };
    

    Then you can use this instance in any member function of class MainWindow, including your slot:

    void MainWindow::on_btnGetDevice_clicked()
    {
       float test = Mesures.CIE_L;
    }
    

    This on the other hand

    void MainWindow::on_btnGetDevice_clicked()
    {
       Measurements Mesures;
       Mesures.CIE_L = 87.45;
    }
    

    is not correct. It would be the same problem you had originally, just in another function. It creates a local variable and it is again destroyed at the end of the function.

    @RogerBreton said:

    First of all, does that mean I will end up with a 'sub-class'? Since I'm declaring a class within a class?

    Not quite. A subclass in C++ is related to inheritance, so in a thing like class MainWindow : public QMainWindow MainWindow is a subclass of QMainWindow, or, in other words, MainWindow inherits QMainWindow.

    What you did is not a subclass but a nested class. A nested class is a class that can be instantiated only in the scope of its parent class, so, in other words, you could only create instances of that class inside of its parent's methods or as a member. You could use a nested class here, but it's not necessary for your code to work and from semantics point of view class Measurements is not really related to MainWindow so it has no business be a nested class of it.

    Btw. please use code blocks ( the </> button ) instead of quotation for code. It's easier to read this way.



  • First, a BIG thank you, Chris, for your generous time.
    I'm glad I had "some" piece of the puzzle right from the beginning.
    I had this code in the mainwindow.h :

    class Measurements{
        QString Sample;
        float CIE_L;
        float CIE_a;
        float CIE_b;
    };
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT    // Macro Meta Object Compiler
        Measurements Mesures;
    

    But I was instantiating the object in the wrong place!
    Your instantiation goes right below the Q_Object constructor -- got it!

    And indeed, the class members are visible from member functions, like so :

    void MainWindow::on_btnGetDevice_clicked()
    {
        Mesures.CIE_L = 89.65;
    }
    

    What about, accessing the variable from other, non-member functions? Like this :

    void SomeClassName::Initialize()
    {
     QTMesures.CIE_L=89.65;
    }
    

    Am I "pushing" my luck? I understand the "boundaries" between classes and the idea of encapsulation. But would there be a way to gain access to the variables, somehow? With Getters and Setters?

    I ask the question because, originally, I could not solve my 'static', global variables problem, and I ended up creating a class for all my static variables, in which I also declared key static functions which, otherwise, because of my technical ignorance, otherwise, weren't "working".



  • @RogerBreton said in Accessing class members:

    What about, accessing the variable from other, non-member functions? Like this :

    Am I "pushing" my luck?

    No. Yes. :)

    Mesures is a member variable of MainWindow. If you could access it all from outside the class you would need a variable holding an instance of the MainWindow whose Mesures you wanted to access, like:

    mainWindowInstance->Mesures.CIE_L=89.65;
    

    But you don't want to be passing a main window handle around, that's not the way to do things. Try not to use statics or global variables.

    In any case you should not be needing code from outside MainWindow which needs to change this value in the main window. Something sounds wrong in the architecture.

    Qt does have a facility where another object might emit a signal and, for example, the main window could connect a slot to listen for it, and perhaps change its member variable Mesures.CIE_L in response. But that is a another matter. It would depend on your actual use case.


  • Moderators

    What about, accessing the variable from other, non-member functions?

    Well, there's couple of components to this answer.
    First of all how do you make a class member visible from outside the class. The simplest and least fancy way is to just make the member public, so:

    class MainWindow : public QMainWindow
    {
        ...
    public:
        Measurements Measures;
    };
    

    And that's perfectly fine for simple uses. If you want that not to be accessed directly you can put it behind a getter. There are multiple reasons you might want to but if you can't think of any don't overthink it :) So like this:

    class MainWindow : public QMainWindow
    {
        ...
    public:
        Measurements& measures();
    private:
        Measurements Measures;
    };
    
    Measurements& MainWindow::measures() { return Measures; }
    

    You could then add const correctness to it but let's keep things simple for now.

    The next thing you need to keep in mind is that a class is just a recipe, or a description of how to construct an instance. An instance, also called an object, is a manifestation of that recipe and there can be zero, one or multiple of them. So when you're asking how to access member of a class from another class the question itself is not quite valid, as classes are abstract things. It's the instances of those classes that can access other instance's members and you need to know which instances are suppose to communicate.
    For example assuming you have a class A and class B and coulpe of instances of those classes:

    A instance1;
    A instance2;
    B instance3;
    B instance4;
    

    and now you want to have a function like instance1.something() that accesses a member of class B. But which instance of it? instance 3 or 4? You need to decide that and one common way to do it is to pass the instance you want to access as an argument, so it would look like instance1.something(instance3);, meaning instance1 of class A would access a member of instance3 of class B. A signature of such function would then look something like this: void A::something(const B& instance_of_B). That's just an example though and there are other ways to communicate between objects and which one is the right one needs to be considered case by case, depending on your application structure.


Log in to reply