0

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;
}
Community
  • 1
  • 1
Xeren Narcy
  • 875
  • 5
  • 15
  • 2
    The first example is **invalid code**, using "implicit int" return type. Function `test` needs a specified return type. Please fix and re-check. – Cheers and hth. - Alf Aug 29 '16 at 16:51
  • 1
    The edited program does not exhibit the behavior you describe. See http://cpp.sh/6rviu – Sebastian Redl Aug 29 '16 at 16:55
  • 1
    With `void` return type the code compiles, but I can not reproduce the implied compiler bug. Anyway, for compiler bugs the compiler and version and complete invocation, verbatim, is relevant. – Cheers and hth. - Alf Aug 29 '16 at 16:55
  • Noted, thanks. I'm aware that doesn't exhibit the problem, i need to add more of the working code to it (didn't think it was involved) – Xeren Narcy Aug 29 '16 at 16:57
  • going to have to close for now, the example i have is littered with macros. TL;DR `base` is also inheriting from a class that has every constructor and operator defined for tracing purposes (non-trivial constructor). basically i need to re-word the whole question, example is just not right for it, sorry... – Xeren Narcy Aug 29 '16 at 17:01

2 Answers2

2

but would this not leak memory? should it be expected to leak?

no. The sequence of operations will be:

derived2::derived2()
  auto p = new int
  base2::base2(p)
   base2::member = p

And for the destructor:

derived2::~derived2() (implied)
 base2::~base2()
  if (base2::member) { delete base2::member; }

One new, one delete. Perfect.

Don't forget to write correct assignment/copy constructors.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

To construct derived class object compiler need to construct its base part. You can specify which base class constructor compiler should use by derived2() : base2(new int).

If you lack such specification, compiler will use base default constructor.

So, base constructor will be called only once, and long as your code did not cause memory leak there wouldn't be any one.

Semyon Burov
  • 1,090
  • 1
  • 9
  • 15
  • Thanks, this is what I thought should happen but I have _somehow_ managed to break that. When I figure out what that is, if anything at all and not something silly on my part, I'll ask again about it specifically. – Xeren Narcy Aug 29 '16 at 17:03
  • About `derived::derived() { base::base(); }`... MSVS really calls `base()` twice. This looks like shortgun... – Semyon Burov Aug 29 '16 at 17:20
  • Clang works same as MSVS and gcc fails to compile with message " cannot call constructor 'D2::B2' directly [-fpermissive]" – Semyon Burov Aug 29 '16 at 17:29