Derek Derek - 2 months ago 24
C++ Question

Copy constructor initialization lists

I know that if you leave a member out of an initialization list in a no-arg constructor, the default constructor of that member will be called.

Do copy constructors likewise call the copy constructor of the members, or do they also call the default constructor?

class myClass {
private:
someClass a;
someOtherClass b;
public:
myClass() : a(DEFAULT_A) {} //implied is b()
myClass(const myClass& mc) : a(mc.a) {} //implied is b(mc.b)??? or is it b()?
}

Answer

Explicitly-defined copy constructors do not call copy constructors for the members.

When you enter the body of a constructor, every member of that class will be initialized. That is, once you get to { you are guaranteed that all your members have been initialized.

Unless specified, members are default-initialized in the order they appear in the class. (And if they can't be, the program is ill-formed.) So if you define your own copy constructor, it's now up to you to call any member copy constructors as desired.

Here is a small program you can copy-paste somewhere and mess around with:

#include <iostream>

class Foo {
public:
    Foo() {
        std::cout << "In Foo::Foo()" << std::endl;
    }

    Foo(const Foo& rhs) {
        std::cout << "In Foo::Foo(const Foo&)" << std::endl;
    }
};

class Bar {
public:
    Bar() {
        std::cout << "In Bar::Bar()" << std::endl;
    }

    Bar(const Bar& rhs) {
        std::cout << "In Bar::Bar(const Bar&)" << std::endl;
    }
};

class Baz {
public:
    Foo foo;
    Bar bar;

    Baz() {
        std::cout << "In Baz::Baz()" << std::endl;
    }

    Baz(const Baz& rhs) {
        std::cout << "In Baz::Baz(const Baz&)" << std::endl;
    }
};

int main() {
    Baz baz1;
    std::cout << "Copying..." << std::endl;
    Baz baz2(baz1);
}

As-is, this prints:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo()
In Bar::Bar()
In Baz::Baz(const Baz&)

Note that it's default-initializing the members of Baz.

By commenting out the explicit copy constructor, like:

/*
Baz(const Baz& rhs) {
    std::cout << "In Baz::Baz(const Baz&)" << std::endl;
}
*/

The output will become this:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo(const Foo&)
In Bar::Bar(const Bar&)

It calls the copy-constructor on both.

And if we reintroduce Baz's copy constructor and explicitly copy a single member:

Baz(const Baz& rhs) :
    foo(rhs.foo)
{
    std::cout << "In Baz::Baz(const Baz&)" << std::endl;
}

We get:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo(const Foo&)
In Bar::Bar()
In Baz::Baz(const Baz&)

As you can see, once you explicitly declare a copy-constructor you are responsible for the copying of all class members; it's your constructor now.

This applies for all constructors, including move constructors.