I've tried searching for articles that relate to this issue, but all I keep finding are articles about people who don't know how to define constructors/can't call the parent constructor/etc and I think this will cut straight to the chase.

The problem code is as follows:

#include <stdio.h>
 
class TestA
{
    public:
    TestA(){}
    TestA(const TestA &Temp)
    {
        printf("TestA called!\n");
    }
};
 
class TestB : public TestA
{
    public:
    TestB(){}
    TestB(const TestA &Temp)
    {
        printf("TestB called!\n");
    }
};
 
int main (const int ArgC, const char *ArgV[])
{
    TestB Test;
    TestB Test2(Test); //Calls TestA's constructor TestA(const TestA &Temp), instead of TestB's
    return 0;
}

A running online example of the code:
http://www.ideone.com/s6npy

The above code (on the GCC compiler) creates the problem where TestB doesn't actually call TestB's constructor, but instead (stupidly) calls TestA's constructor (I say stupid because if TestA's was intended it could be called from within TestB's constructor), and completely bypasses TestB's constructor in the process (you can even comment out TestB's constructors and it will still compile without errors).

Obviously, given constructors can't be made virtual, and defining either or both of the constructors as explicit is redundant (as it's called explicitly), I cannot see (and cannot find an article on) how I can force the compiler/program to choose TestB's constructor (when constructing TestB) over TestA's constructor.

I'm personally surprised because reading a lot of posts regarding constructors, I got the distinct impression the subclass constructor should be called first, but I find this isn't what is happening.

How can I resolve this issue?

Edited 5 Years Ago by SSight3: n/a

I am not sure whether or not to edit this into the original post, but as an additional comment, commenting out TestA's TestA(const TestA &Temp) constructor doesn't make the compiler/program refer to TestB's, but rather, it uses the default (const TestA &Temp) instead, example of which is here:
http://ideone.com/8eSB5

Even more perplexingly, with the no-arguments constructor, both TestA then TestB's arguments are called, but this isn't the case with the second (const TestA &Temp) constructor.

Why is there such inconsistent behaviour?

Edited 5 Years Ago by SSight3: n/a

>> The above code (on the GCC compiler) creates the problem where TestB doesn't actually call TestB's constructor,

I does call the TestB constructor, just not the one you defined but the default, compiler-generated copy-constructor.

If you don't provide a constructor of the form TestB(const TextB&) , the compiler will generate a default one (a copy-constructor) and that default version of it will call the copy-constructor of the base-class (either default or user-defined), this is why your TestA(const TestA&) constructor gets called.

>> I say stupid because if TestA's was intended it could be called from within TestB's constructor

One TestA constructor or another will always be called by the derived class constructor (whether you specify it or not) because C++ doesn't allow objects to be only partly constructed.

>> and completely bypasses TestB's constructor in the process

It doesn't, it uses the compiler-generated default version of the copy-constructor, simply because if you don't provide a copy-constructor (or disable it), the compiler will generate one for you. The constructor of TestB is not by-passed, it never will be in C++, that's a guarantee that C++ standard (and any decent compiler) gives you.

>> given constructors can't be made virtual,

Obviously. By definition, when you construct an object, you know its final type, and the concept of virtual dispatching is meaningless.

>> defining either or both of the constructors as explicit is redundant (as it's called explicitly)

Explicit is only meaningful for single-parameter constructors, to prevent implicit conversions. Of course, in the case of a copy-constructor, there isn't much point in making it explicit either.

>> how I can force the compiler/program to choose TestB's constructor

With your current code, this should force the call of TestB's constructor:

int main (const int ArgC, const char *ArgV[])
{
    TestB Test;
    TestA& ref_Test = Test;
    TestB Test2(ref_Test); //Should call TestB's constructor.

    // or:
    TestB Test3( static_cast< TestA& >(Test) );
    return 0;
}

Another way is to disable the default copy-constructor in the TestB class:

class TestB : public TestA
{
    private:
    TestB(const TestB&);  //non-copyable.
    TestB& operator=(const TestB&); //non-assignable.
    public:
    TestB(){}
    TestB(const TestA &Temp)
    {
        printf("TestB called!\n");
    }
};

>> I got the distinct impression the subclass constructor should be called first, but I find this isn't what is happening.

It is exactly what is happening.


>> How can I resolve this issue?

Learn to understand the special member functions of a class, that is, the copy-constructor, the copy-assignment operator, and the destructor (and for C++11, the move-constructor and move-assignment operator). All of them will be generated by the compiler if you don't provide them (the idea is that if you follow good coding practices, you rarely have to provide them, read my tutorial).


>> Even more perplexingly, with the no-arguments constructor, both TestA then TestB's arguments are called

This is exactly what you expect should happen. The base-class is constructed before the body of the derived-class constructor is entered, this is always the case whether you specify it (in the initialization list) or not.

>> but this isn't the case with the second (const TestA &Temp) constructor.

Yes it is, you just don't see it because the TestB constructor that is used in this case is the compiler-generated one.

>> Why is there such inconsistent behaviour?

This behaviour is neither surprising nor inconsistent. All you have witnessed and reported is exactly the behaviour that any knowledgeable C++ programmer would expect and the behaviour that the C++ standard mandates.

This question has already been answered. Start a new discussion instead.