Regarding this question and the answer to it there does seem to be an exception, but it raised more questions for me than answering them. Consider this:
#include <iostream>
using namespace std;
struct base {
virtual void test() {cout << "base::test" << endl;}
base() {test();}
virtual ~base() {}
};
struct derived : base {
virtual void test() {cout << "derived::test" << endl;}
derived() : base() {}
~derived() {}
};
int main() {
derived d;
return 0;
}
I naively thought this would only print either of the two messages. It actually prints both - first the base version then derived. This behaves the same on -O0
and -O3
settings, so it's not an optimization or lack thereof as far as I can tell.
Am I to understand that calling base
(or higher / earlier classes' constructors) within a derived
constructor, will not prevent the default base
constructor (or otherwise) from being called beforehand?
That is to say, the sequence in the above snippet when constructing a derived
object is: base()
then derived()
and within that base()
again?
I know it doesn't make sense to modify the vtable just for the purposes of calling base::base()
, back to what it was before derived::derived()
was called, just for the sake of calling a different constructor. I can only guess that vtable-related things are hard-coded into the constructor-chain and calling previous constructors is literally interpreted as a proper method call (up to the most-derived object having been constructed in the chain so far)?
These minor questions aside, it raises two important ones:
1. Is calling a base constructor within a derived constructor always going to incur calling the default base constructor prior to the derived constructor being called in the first place? Is this not inefficient?
2. Is there a use-case where the default base constructor, per #1, shouldn't be used in lieu of the base constructor explicitly called in a derived classes' constructor? How can this be achieved in C++?
I know #2 sounds silly, after all you'd have no guarantee the state of the base class part of a derived class was 'ready' / 'constructed' if you could defer calling the base constructor until an arbitrary function call in the derived constructor. So for instance this:
derived::derived() { base::base(); }
... I would expect to behave the same way and call the base constructor twice. However is there a reason that the compiler seems to treat it as the same case as this?
derived::derived() : base() { }
I'm not sure. But these seem to be equivalent statements as far as observed effects go. It runs counter to the idea I had in mind that the base constructor could be forwarded (in a sense at least) or perhaps a better choice of word would be selected within a derived class using :base()
syntax. Indeed, that notation requires base classes to be put before members distinct to the derived class...
In other words this answer and it's example (forget for a moment its C#) would call the base constructor twice? Although I understand why it would be doing that, I don't understand why it doesn't behave more "intuitively" and select the base constructor (at least for simple cases) and call it only once.
Isn't that a risk of double-initializing the object? Or is that part-and-parcel of assuming the object is uninitialized when writing constructor code? worst case do I now have to assume that every class member could potentially be initialized twice and guard against that?
I'll end with a horrible example - but would this not leak memory? should it be expected to leak?
#include <iostream>
using namespace std;
struct base2 {
int * member;
base2() : member(new int) {}
base2(int*m) : member(m) {}
~base2() {if (member) delete member;}
};
struct derived2 : base2 {
derived2() : base2(new int) {
// is `member` leaking?
// should it be with this syntax?
}
};
int main() {
derived2 d;
return 0;
}
Aucun commentaire:
Enregistrer un commentaire