What is the most efficient and clean way to access specific class methods from another class
-
Hello. I am new to C++ and would like some advice on how to keep my code clean and simple.
I am mainly interesting in knowing what do experienced c++ developers do when they need to access functions or methods from another class.
Lets Imagine I have MainWindow.cpp and MainWindow.h which is my main class where a lot of the things are happening. Inside MainWindow.cpp, I need to be able to access most of my other classes in my program.
So what I normally do:
int main(int argc, char *argv[]) { QApplication a(argc, argv); Serial s; Logging l; SettingsWindow set; CommandWindow cmd; TestTool t(&s,&set,&cmd); MainWindow w(&s,&l,&t,&set,&cmd); w.show();
And for example my MainWindow class constructor looks like:
MainWindow::MainWindow(Serial* serial_ptr,Logging* logging_ptr,TestTool* testtool_ptr,SettingsWindow* settings_ptr,CommandWindow* command_ptr,QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow) { command_local = command_ptr; settings_local = settings_ptr; serial_local = serial_ptr; logging_local = logging_ptr; testtool_local = testtool_ptr; }
As you can see from above, I pass a other class objects to MainWindow and create a pointer that points to that object. Now in my mainwindow I can access private members and methods using testtool_local , logging_local , serial_local , settings_local , command_local
I am very curious to know whether this is a good way of doing this or I should be using some other method for example using friend class declaration or some other methods available.. Thanks in advance :)
-
where do I start?
This is indicative of not having a good understanding of OO programming and your C language bias is slipping thru by using pointers instead of references...Finally, you really need to consider the lifetime of object when you do choose to pass them by pointer. What happens if you declare a local objct o, then pass its address to a higher level (global) object parent(&o)? When o goes away parent may still think it is accessible by pointer.
Spend some time learning OO programming from an academtic (student) perspective.
-
Hello. I am new to C++ and would like some advice on how to keep my code clean and simple.
I am mainly interesting in knowing what do experienced c++ developers do when they need to access functions or methods from another class.
Lets Imagine I have MainWindow.cpp and MainWindow.h which is my main class where a lot of the things are happening. Inside MainWindow.cpp, I need to be able to access most of my other classes in my program.
So what I normally do:
int main(int argc, char *argv[]) { QApplication a(argc, argv); Serial s; Logging l; SettingsWindow set; CommandWindow cmd; TestTool t(&s,&set,&cmd); MainWindow w(&s,&l,&t,&set,&cmd); w.show();
And for example my MainWindow class constructor looks like:
MainWindow::MainWindow(Serial* serial_ptr,Logging* logging_ptr,TestTool* testtool_ptr,SettingsWindow* settings_ptr,CommandWindow* command_ptr,QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow) { command_local = command_ptr; settings_local = settings_ptr; serial_local = serial_ptr; logging_local = logging_ptr; testtool_local = testtool_ptr; }
As you can see from above, I pass a other class objects to MainWindow and create a pointer that points to that object. Now in my mainwindow I can access private members and methods using testtool_local , logging_local , serial_local , settings_local , command_local
I am very curious to know whether this is a good way of doing this or I should be using some other method for example using friend class declaration or some other methods available.. Thanks in advance :)
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
what do experienced c++ developers do when they need to access functions or methods from another class
They just call those. It is really as simple as that :-)
Of course you need the instance of the class on which you want to call the non-static method."Now in my mainwindow I can access private members" - no, you can't! You can access public members. And you also should never try to access private members - there is a reason why those are private! Why do you want to access private members?
-
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
what do experienced c++ developers do when they need to access functions or methods from another class
They just call those. It is really as simple as that :-)
Of course you need the instance of the class on which you want to call the non-static method."Now in my mainwindow I can access private members" - no, you can't! You can access public members. And you also should never try to access private members - there is a reason why those are private! Why do you want to access private members?
@jsulm
Yes my bad I meant public members and methods!So are you suggesting that there is nothing really wrong with doing it this way ?
-
where do I start?
This is indicative of not having a good understanding of OO programming and your C language bias is slipping thru by using pointers instead of references...Finally, you really need to consider the lifetime of object when you do choose to pass them by pointer. What happens if you declare a local objct o, then pass its address to a higher level (global) object parent(&o)? When o goes away parent may still think it is accessible by pointer.
Spend some time learning OO programming from an academtic (student) perspective.
@Kent-Dorfman
Thanks for the reply. Would you be able to clarify a little bit? Could you point me in the right direction ( For example some example or some good reading material to start with). Simply saying "learn OO programming" is not really helpful. -
@Kent-Dorfman
Thanks for the reply. Would you be able to clarify a little bit? Could you point me in the right direction ( For example some example or some good reading material to start with). Simply saying "learn OO programming" is not really helpful.@lukutis222 No, there is nothing wrong with that.
-
@jsulm
Yes my bad I meant public members and methods!So are you suggesting that there is nothing really wrong with doing it this way ?
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
So are you suggesting that there is nothing really wrong with doing it this way ?
Well, there are several layers to unpack how to improve on your solution.
First of all, sticking to your current approach, there is one major remark on C++ in general. You should initialize your member variables instead of assigning them. For your constructor this would mean:
MainWindow::MainWindow(Serial* serial_ptr,Logging* logging_ptr,TestTool* testtool_ptr,SettingsWindow* settings_ptr,CommandWindow* command_ptr,QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow), command_local(command_ptr), settings_local(settings_ptr), serial_local(serial_ptr), logging_local(logging_ptr), testtool_local(testtool_ptr) { }
In your specific case with pointers this might not make such a huge performance difference (if I am not mistaken there is none), but in the general case it might lead to worse performance. This is because all member variables will be initialized if you don't do it yourself (I believe it is default initialization which for pointers means they are uninitialized (somebody correct me if I'm wrong!)). So, in the general case you would have first default initialization (e.g. through a default constructor) and following that you would have assignment (which would then override the default values previously assigned). Using the approach I proposed it is just one constructor call and no assignment.
Second, @Kent-Dorfman already mentioned to use references instead of pointer whenever possible. This would actually force you to use initialization instead of assignment.
Third: Is there any good reason why
Serial
,Logging
,SettingsWindow
,CommandWindow
, andTestTool
are instantiated outside ofMainWindow
? It might make more sense to create them as member variables of MainWindow directly (the C++, non-Qt-specific way):MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow), command_local(CommandWindow()), settings_local(SettingsWindow()), serial_local(Serial()), logging_local(Logging()), testtool_local(&serial_local, &settings_local, &command_local) { }
(Somebody help me out with the syntax for the default constructor in this context (I am trying to think too hard if this is right). Is it
command_local(CommandWindow())
? Or could I just writecommand_local()
instead? Probably best to just leave it out to have the default constructor called...)This approach (which would apply to C++ in general) would have everything as regular member variables instead of pointers. However, we are talking about Qt here and this means that a few things might be done differently. I assume that
SettingsWindow
andCommandWindow
inherit from QWidget. So, most likely MainWindow should be their parent. Here now comes the Qt way: If you make MainWindow the parent of these objects, you must use pointers as MainWindow will try to delete all its children. Then, your windows would be back to being pointers and you would need to writecommand_local(new CommandWindow(this))
instead. If your other classes inherit from QObject, QWidget, or similar, these also should be pointers and have MainWindow as their parent.That being said:
Logging
could be considered more like a global object where only one instance exists. Traditionally, this would call for the Singleton pattern instead. As far as I understood you shouldn't use the Singleton pattern anymore as it prevents proper testing. If I remember correctly, it's this talk I watched on what to use instead: https://www.youtube.com/watch?v=K5c7uvWe_hwFourth: Well, I would expect that the SettingsWindow is not always visible. The usual way in Qt is that you would click a button or click inside the menu to open up the settings dialog. In that slot handling this you would create a SettingsWindow for the duration of this single slot and delete it afterwards. The larger your project gets the more windows/dialogs you will have and it might take several minutes to instantiate them all right at start up (been there, done that). Also, they will eat up a lot of memory. So, as a general guideline: For all widgets which are only shown temporarily (which would then actually be called dialogs) create them when you need them and destroy them afterwards.
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
I am mainly interesting in knowing what do experienced c++ developers do when they need to access functions or methods from another class.
If we were talking just about plain C++ we would be done at this point. But, we are talking about Qt as well. The main approach of Qt is to use signals and slots. In many cases these replace accessing methods from other classes (to some extent). Let's get back to the
SettingsWindow
for an example. I would expect to have aSettings
class as well which holds variables with the current settings. There would be a member variable inMainWindow
of typeSettings
(preferably not a pointer!). When the SettingsWindow (supposing it is a dialog) is opened a new object of this class is created and gets a reference toSettings
in its constructor. This data is used to initialize the SettingsWindow. Settings and slots could be used to forward any changes in SettingsWindow to the actual Settings. Now, MainWindow would still use methods from Settings to get access to the current settings. What we have here is thatSettings
is your model andSettingsWindow
is your view.Not knowing your application this is just a lot of guesses what to do better. So, in your specific application you need to decide which approach makes sense for which class.
-
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
So are you suggesting that there is nothing really wrong with doing it this way ?
Well, there are several layers to unpack how to improve on your solution.
First of all, sticking to your current approach, there is one major remark on C++ in general. You should initialize your member variables instead of assigning them. For your constructor this would mean:
MainWindow::MainWindow(Serial* serial_ptr,Logging* logging_ptr,TestTool* testtool_ptr,SettingsWindow* settings_ptr,CommandWindow* command_ptr,QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow), command_local(command_ptr), settings_local(settings_ptr), serial_local(serial_ptr), logging_local(logging_ptr), testtool_local(testtool_ptr) { }
In your specific case with pointers this might not make such a huge performance difference (if I am not mistaken there is none), but in the general case it might lead to worse performance. This is because all member variables will be initialized if you don't do it yourself (I believe it is default initialization which for pointers means they are uninitialized (somebody correct me if I'm wrong!)). So, in the general case you would have first default initialization (e.g. through a default constructor) and following that you would have assignment (which would then override the default values previously assigned). Using the approach I proposed it is just one constructor call and no assignment.
Second, @Kent-Dorfman already mentioned to use references instead of pointer whenever possible. This would actually force you to use initialization instead of assignment.
Third: Is there any good reason why
Serial
,Logging
,SettingsWindow
,CommandWindow
, andTestTool
are instantiated outside ofMainWindow
? It might make more sense to create them as member variables of MainWindow directly (the C++, non-Qt-specific way):MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent}, ui(new Ui::MainWindow), command_local(CommandWindow()), settings_local(SettingsWindow()), serial_local(Serial()), logging_local(Logging()), testtool_local(&serial_local, &settings_local, &command_local) { }
(Somebody help me out with the syntax for the default constructor in this context (I am trying to think too hard if this is right). Is it
command_local(CommandWindow())
? Or could I just writecommand_local()
instead? Probably best to just leave it out to have the default constructor called...)This approach (which would apply to C++ in general) would have everything as regular member variables instead of pointers. However, we are talking about Qt here and this means that a few things might be done differently. I assume that
SettingsWindow
andCommandWindow
inherit from QWidget. So, most likely MainWindow should be their parent. Here now comes the Qt way: If you make MainWindow the parent of these objects, you must use pointers as MainWindow will try to delete all its children. Then, your windows would be back to being pointers and you would need to writecommand_local(new CommandWindow(this))
instead. If your other classes inherit from QObject, QWidget, or similar, these also should be pointers and have MainWindow as their parent.That being said:
Logging
could be considered more like a global object where only one instance exists. Traditionally, this would call for the Singleton pattern instead. As far as I understood you shouldn't use the Singleton pattern anymore as it prevents proper testing. If I remember correctly, it's this talk I watched on what to use instead: https://www.youtube.com/watch?v=K5c7uvWe_hwFourth: Well, I would expect that the SettingsWindow is not always visible. The usual way in Qt is that you would click a button or click inside the menu to open up the settings dialog. In that slot handling this you would create a SettingsWindow for the duration of this single slot and delete it afterwards. The larger your project gets the more windows/dialogs you will have and it might take several minutes to instantiate them all right at start up (been there, done that). Also, they will eat up a lot of memory. So, as a general guideline: For all widgets which are only shown temporarily (which would then actually be called dialogs) create them when you need them and destroy them afterwards.
@lukutis222 said in What is the most efficient and clean way to access specific class methods from another class:
I am mainly interesting in knowing what do experienced c++ developers do when they need to access functions or methods from another class.
If we were talking just about plain C++ we would be done at this point. But, we are talking about Qt as well. The main approach of Qt is to use signals and slots. In many cases these replace accessing methods from other classes (to some extent). Let's get back to the
SettingsWindow
for an example. I would expect to have aSettings
class as well which holds variables with the current settings. There would be a member variable inMainWindow
of typeSettings
(preferably not a pointer!). When the SettingsWindow (supposing it is a dialog) is opened a new object of this class is created and gets a reference toSettings
in its constructor. This data is used to initialize the SettingsWindow. Settings and slots could be used to forward any changes in SettingsWindow to the actual Settings. Now, MainWindow would still use methods from Settings to get access to the current settings. What we have here is thatSettings
is your model andSettingsWindow
is your view.Not knowing your application this is just a lot of guesses what to do better. So, in your specific application you need to decide which approach makes sense for which class.
@SimonSchroeder
May I make a personal 2 cents comment here? I am willing to be shot down or convinced otherwise.But, maybe my own preference, I do not like these (potentially) long initializer lists for member variables being written in a comma-list against the declaration part of constrcutors. Even what you have here is too long for me, and I have seen others with more member variables so the list goes on and on. For me it hinders readability when looking in an editor. I am happy to see the
QMainWindow{parent}
and theui(new Ui::MainWindow)
, they tell me something basic about the class constructor, but the rest is too distracting.Putting these in the body instead makes the editing/folding experience cleaner. And I can intersperse comments, I can have a bit of code (e.g. maybe I'll want an
if
statement where I would have to use? :
in the expression list), and so on.As for the efficiency versus what C++ if I don't specify an initializer vs assignment. Well, for pointers and simple types like
int
etc. they are all uninitialized in a class instance anyway, so I'm not losing efficiency without an initializer. So that covers the majority of my member variables, only those which are classes/structs and not pointers are left over.So I get the marginal difference in efficiency in a few cases, but otherwise it's just cumbersome for me. Want to convince me otherwise/to change my ways? :)
-
@SimonSchroeder
May I make a personal 2 cents comment here? I am willing to be shot down or convinced otherwise.But, maybe my own preference, I do not like these (potentially) long initializer lists for member variables being written in a comma-list against the declaration part of constrcutors. Even what you have here is too long for me, and I have seen others with more member variables so the list goes on and on. For me it hinders readability when looking in an editor. I am happy to see the
QMainWindow{parent}
and theui(new Ui::MainWindow)
, they tell me something basic about the class constructor, but the rest is too distracting.Putting these in the body instead makes the editing/folding experience cleaner. And I can intersperse comments, I can have a bit of code (e.g. maybe I'll want an
if
statement where I would have to use? :
in the expression list), and so on.As for the efficiency versus what C++ if I don't specify an initializer vs assignment. Well, for pointers and simple types like
int
etc. they are all uninitialized in a class instance anyway, so I'm not losing efficiency without an initializer. So that covers the majority of my member variables, only those which are classes/structs and not pointers are left over.So I get the marginal difference in efficiency in a few cases, but otherwise it's just cumbersome for me. Want to convince me otherwise/to change my ways? :)
My best recommendation is to read a book on SOLID coding techniques SOLID stands for five principles:
- Single responsibility principle (i.e. each function or class is responsible for one thing, and one thing only)
- Open-closed principle (Code should be open to extension but closed to change - make it easy to add new features, but don't change behavior on existing ones)
- Liskov Substitution principle (More or less: A derived class should never have a narrower interface than it's base)
- Interface segregation principle (Use separate interfaces for different usages/clients of a class)
- Dependency inversion principle (Instead of using a fixed class, let someone pass an interface to you)
"Clean C++" would be such a book. It has a somewhat patronizing tone, but contains a lot of useful and practical approaches.
-
@SimonSchroeder
May I make a personal 2 cents comment here? I am willing to be shot down or convinced otherwise.But, maybe my own preference, I do not like these (potentially) long initializer lists for member variables being written in a comma-list against the declaration part of constrcutors. Even what you have here is too long for me, and I have seen others with more member variables so the list goes on and on. For me it hinders readability when looking in an editor. I am happy to see the
QMainWindow{parent}
and theui(new Ui::MainWindow)
, they tell me something basic about the class constructor, but the rest is too distracting.Putting these in the body instead makes the editing/folding experience cleaner. And I can intersperse comments, I can have a bit of code (e.g. maybe I'll want an
if
statement where I would have to use? :
in the expression list), and so on.As for the efficiency versus what C++ if I don't specify an initializer vs assignment. Well, for pointers and simple types like
int
etc. they are all uninitialized in a class instance anyway, so I'm not losing efficiency without an initializer. So that covers the majority of my member variables, only those which are classes/structs and not pointers are left over.So I get the marginal difference in efficiency in a few cases, but otherwise it's just cumbersome for me. Want to convince me otherwise/to change my ways? :)
@JonB said in What is the most efficient and clean way to access specific class methods from another class:
So I get the marginal difference in efficiency in a few cases, but otherwise it's just cumbersome for me. Want to convince me otherwise/to change my ways? :)
In general I prefer initialization over assignment (this also holds true when declaring a variable). My best sell for using the initializer list was performance. I tend to cut down on the length of the initializer list by providing default initializations when declaring member variables (since C++11). Also delegating constructors might help in general. (Both approaches do not help in this specific example.)
When I am in a good mood I also try to utilize const-correctness (declaring all variables as const if possible). Sometimes member variables can also be const (especially if it is a const-reference). In that case you are forced to use the initializer list.
Since you are an experienced C++ programmer I would not try to convince you to do it differently. However, because of edge cases I would rather teach to always use the initializer list. Beginners will not figure out immediately why their program might be slow (because they have both initializiation and assignment). And they might also forget that for const members they need to inititialize instead of assign. Saves them a lot of trouble if they always use initializer lists.