3

In the VideoWidget and the Secure Socket Client examples in Qt, the code presented there initalises the child widgets in the parent widgets, like so:

SslClient::SslClient(QWidget *parent)
: QWidget(parent), socket(0), padLock(0), executingDialog(false)

and

VideoPlayer::VideoPlayer(QWidget *parent)
: QWidget(parent)
, mediaPlayer(0, QMediaPlayer::VideoSurface)
, playButton(0)
, positionSlider(0)
, errorLabel(0)

However, further down the code, I see the following:

playButton = new QPushButton;

or in the case of the Secure Socket Client, this:

padLock = new QToolButton;

Why initalise in the constructor when it will be initalised in the code?

László Papp
  • 51,870
  • 39
  • 111
  • 135
Skaty
  • 467
  • 2
  • 6
  • 19
  • 2
    This seems to be a misunderstanding of the syntax: http://stackoverflow.com/questions/2785612/c-what-does-the-colon-after-a-constructor-mean – Peter Feb 23 '14 at 05:50
  • @Peter, what? I think the OP perfectly understands the colon, but is questioning the duplication of the `padLock(0)` in the initializer list and `padLock = new ...;` – Alexis Wilke Feb 23 '14 at 06:41
  • @AlexisWilke, I edited the question, because by saying inheritance, I meant initalising. :o – Skaty Feb 23 '14 at 06:56

3 Answers3

3

Why initalise in the constructor when it will be initalised in the code?

So that the implementation is exception safe. Suppose that you'd have this code:

SslClient::SslClient(QWidget *parent)
: QWidget(parent), socket(0), padLock((QToolButton*)(0x0BEEF)), executingDialog(false) {
  throw std::exception();
  padLock = new QToolButton;
}

The destructor will delete padLock, but it has junk value, and you have undefined behavior. Recall that deleting a nullptr is safe (as is calling free(NULL) in C!). The junk padlock value shows what happens when you don't initialize it. Throwing demonstrates that some intervening code may throw. Specifically, any intervening new will throw if the allocation can't succeed. new does not return on failure (as in: it throws std::bad_alloc instead of returning, so the notion of return value doesn't apply at all).

If one is writing idiomatic C++, then the pointer should not be a naked pointer, but a std::unique_ptr or QScopedPointer, and then this problem vanishes. You don't have to remember to initialize the pointer to zero, and you don't have to remember to clean it up. RAII gives you a lot of win. That's the secret weapon that you get when you really use C++ the way it's meant to be used.

The C++ RAII idiom doesn't exist per se in any other common programming language. In languages that allow it (such as Java, C#, F#, Python, OCaml, and Common Lisp), the idiomatic work-around is to define a higher-order with_resource function, see examples for OCaml, Java and Python and Python again. Basically, in languages other than C++, and especially in garbage-collected languages, memory resource deallocation is handled differently from non-memory resource deallocation. In C++, those are united under the RAII umbrella.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Have not done C++ for a while. Not really sure right now, at least I cannot give a link. Sorry about the comments (already deleted them). – Mare Infinitus Feb 23 '14 at 18:27
  • @MareInfinitus Throwing from constructors is normal. Throwing from a destructor should be assumed to be equivalent to calling [`terminate()`](http://en.cppreference.com/w/cpp/error/terminate) and must be done only when things are obviously unrecoverable bad (say memory corruption). – Kuba hasn't forgotten Monica Feb 23 '14 at 18:43
  • Note that Qt does not use exceptions, and there is no exception handling (rightfully) in the example either. `The C++ RAII idiom doesn't exist per se in any other common programming language` -> Actually, it does, for instance python. In fact, C++ does not have full RAII idiom, unlike python. – László Papp Feb 23 '14 at 19:29
  • @LaszloPapp Qt has no way of not using exceptions: when `new` fails, it throws, and quite a bit of critical core Qt code seems to be written not to leak when that happens. AFAIK, python requires the `with` construct to prevent resource leaks - or is there something I don't know? My Python-fu is very rudimentary. – Kuba hasn't forgotten Monica Feb 23 '14 at 19:47
  • Be my guest, and show me where qt throws an exception in such cases, then. :) I do not see any non-qt exception'ish code in the example either. Also, 99% of the cases, memory allocation failure is not a concern, especially since Qt does not run on really resource critical systems. Hence, I personally consider it bad practice. Note that, in the future, this issue will go away with the in-place initialization right in the declaration with uniform initialization. As for python, you need to have an explicit construction for acquire and release. C++ does not have really have proper RAII. – László Papp Feb 23 '14 at 20:09
  • It just accidentally works for certain cases, but miserably fails for others, unfortunately (could not type this all in one comment). – László Papp Feb 23 '14 at 20:14
  • Yes, the allocation can fail in 0.0001% of the cases, but I am not sure it is a main concern in an example provided for the majority. In most cases, it might actually slow the program down. The typical case is to write `a (new A)`, I believe. – László Papp Feb 23 '14 at 20:29
0

It is good practice. You should always initialize your variables, even if it looks useless.

This goes for local variables too. It is a good practice.

Actually, if you do this:

QString str;

Then str is automatically initialized. Unfortunately, if you do this:

int value;

Then value is not initialized to anything. It's good for speed, but can often lead to bugs.

In case of a constructor, it is a good idea because if you change the code in the body, you may end up not initializing a variable or another... which could have been at least set to null in the list of initializers.

There is also a reason in link with exceptions, but that's probably beyond Qt which doesn't use exceptions much.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • I am not sure if it really is a "good" practice. I would say, it is just one practice of those. For instance, the kernel developers would probably say that it is "bad" practice. ;-) – László Papp Feb 23 '14 at 10:54
  • Yes. Some developers think that initializing is waste of time. Although in many cases compilers will "fix" the problem by removing the first initialization whenever possible. So `int value(0);` followed by `value = 9;` will remove in one move of 9 in value's location. – Alexis Wilke Feb 23 '14 at 19:23
  • It is possible, but it is not guaranteed by the standard, so it is up to the implementations. – László Papp Feb 23 '14 at 19:26
0

Why initalise in the constructor when it will be initalised in the code?

I believe it is just for the reason that if the actual instantiation is deferred to a different method later, for intance, you will not get an uninitialized member in the middle of some operation causing undefined behavior.

However, in this particular case, it does not make much sense if the code is not planned to be modified any soon.

It might also boil down to coding styles in different projects, but in this special case, I am not aware of this being constrained by the official Qt coding style. For instance, in the linux kernel, they prefer not to initialize it like this.

László Papp
  • 51,870
  • 39
  • 111
  • 135