This really isn’t a C++ 11 or Qt thing we need to discuss next. It is something which hoses every C/C++ programmer at some point, especially if you are either tired or reading fast.
Where const is matters
char txt[] = "Red baby buggy bumpers"; char *ptr = txt; // non-const pointer, non-const data const char *ptr = txt; // non-const pointer, const data char * const ptr = txt; // const pointer, non-const data const char * const ptr = txt; // const pointer, const data
The often forgotten rule is const to the left of * means what is pointed at is constant while const to the right of * means the pointer is constant. Even I get in a hurry and get burned by this. If you choose to work with the more advanced classes in Qt or wish to create your own delegates for things, you need to memorize this mantra and have this little post bookmarked.
Now that we have C++ 11 a good many of the class butchering we had to do in the past need not exist. Oh come on, if you have been coding in C++ for a while you have seen the enum hack.
class MyClass { enum { MAX_ENTRIES = 99}; int entries[ MAX_ENTRIES]; ... }
Then the language moved on to the Texas Two Step. This required you declare a constant as static const in the class definition:
class MyOtherClass { static const double smoothing; ... };
Then in the class implementation (.cpp) file you made this declaration:
const double MyOtherClass = 0.456;
Finally! We can initialize variables in our class declarations.
#ifndef TEST2CLASS_H #define TEST2CLASS_H #include <string> class Test2Class { public: ~Test2Class(); explicit Test2Class(); explicit Test2Class( const std::string& msg); explicit Test2Class( double ratio); explicit Test2Class( int kount); std::string dumpValues(); private: int m_kount=10; double m_ratio=3.45; std::string m_msg{"default message"}; }; #endif // TEST2CLASS_H
While it may look unfamiliar to see values assigned during member variable declaration it makes things so much cleaner. Yes, we used to have constructors with default values but when we did you had to either provide every value in the list just to change the last one or you had to perform a multi-step construction by calling the constructor then N set methods to change the few values at the end. It was even worse if you needed multiple constructors because you had to replicate code or create a common shared method for each constructor to call. The final wrench in the gears was when a default value changed in a later release. This meant you had to search for all of those places where you passed in several default values just to change the one you needed to change.
#include "Test2Class.h" #include <iostream> #include <sstream> Test2Class::Test2Class( int kount) { m_kount = kount; std::cout << "Constructor (int): kount: " << m_kount << " ratio: " << m_ratio << " msg: " << m_msg << std::endl; } Test2Class::Test2Class( double ratio) { m_ratio = ratio; std::cout << "Constructor (double): kount: " << m_kount << " ratio: " << m_ratio << " msg: " << m_msg << std::endl; } Test2Class::Test2Class( const std::string& msg) { m_msg = msg; std::cout << "Constructor (std::string): kount: " << m_kount << " ratio: " << m_ratio << " msg: " << m_msg << std::endl; } Test2Class::~Test2Class() { } std::string Test2Class::dumpValues() { std::string txt; std::stringstream txtStream( txt); txtStream << "Kount: " << m_kount << " Ratio: " << m_ratio << " Msg: " << m_msg; return txtStream.str(); }
Yes, there is a rule of thumb saying one should use : member_variable( param) on the constructor declaration for direct assignments. When a class has C++ 11 member initialization in the class definition I humbly suggest one avoids that particular rule of thumb.
#include "Test2Class.h" #include <iostream> int main(int argc, char **argv) { Test2Class b( 16); Test2Class c( 3.1435); Test2Class d( "new message"); std::cout << "Values for b: " << b.dumpValues() << std::endl; std::cout << "Values for c: " << c.dumpValues() << std::endl; std::cout << "Values for d: " << d.dumpValues() << std::endl; return 0; }
Running our program in the CodeLite IDE produces the following:
Constructor (int): kount: 16 ratio: 3.45 msg: default message Constructor (double): kount: 10 ratio: 3.1435 msg: default message Constructor (std::string): kount: 10 ratio: 3.45 msg: new message Values for b: Kount: 16 Ratio: 3.45 Msg: default message Values for c: Kount: 10 Ratio: 3.1435 Msg: default message Values for d: Kount: 10 Ratio: 3.45 Msg: new message Press ENTER to continue...
I added the seemingly needless debug prints in the constructor as a lead in to our next post.