21

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()?
}
1

8 Answers 8

33

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.

Sign up to request clarification or add additional context in comments.

4 Comments

What if a member is a raw pointer (e.g. void*) or int, double, etc. - will the user-defined copy constructor assign 0 to them before entering { if user does not assign anything to these members in the initialization list of the copy constructor?
@SergeRogatch: If you don't initialize it explicitly, the value is unspecified like a regular uninitialized variable, and reading it is undefined behavior. You must explicitly initialize pointers to null, ints to 0, etc.
"Unless specified, members are default-initialized in the order they appear in the class." isn't it the case that only non POD members are default-initialized?
@pellucidcoder Everything will be default-initialized. For non-class types this does nothing. For class types (POD or not) overload resolution of default constructors is performed. See also en.cppreference.com/w/cpp/language/default_initialization
2

For any member variable having a default constructor that default constructor is invoked if you have not explicitly added any other constructor call for that member variable into the initialization list.

Comments

2

For details see: Is there an implicit default constructor in C++?

Short:

  • Compiler Generated "Default Constructor": uses the default constructor of each member.
  • Compiler Generated "Copy Constructor": uses the copy constructor of each member.
  • Compiler Generated "Assignment Operator": uses the assignment operator of each member.

1 Comment

doenst really the answer, OPs copy constructor in question isnt compiler generated.
1

Yes. Ctors are ctors.

2 Comments

-1: What does "Yes" mean as an answer to a question containing an "or"?
rstevens, the question was edited shortly after Charlie answered. Charlie answered the original question perfectly. That said, I have edited my answer below and I think it is good enough :)
1

There is nothing magical about a copy constructor, other than that the compiler will add it in if needed. But in how it actually runs, there is nothing special - if you don't explicitly say "use such and such a constructor", it'll use the default.

Comments

1

Not in VC9. Not sure about the others.

// compiled as: cl /EHsc contest.cpp
//
//    Output was:
//    Child1()
//    -----
//    Child1()
//    Child2()
//    Parent()
//    -----
//    Child1(Child1&)
//    Child2()
//    Parent(Parent&)

#include <cstdio>

class Child1 {
    int x;
public:
    static Child1 DEFAULT;

    Child1(){
        x = 0;
        printf("Child1()\n");
    }

    Child1(Child1 &other){
        x = other.x;
        printf("Child1(Child1&)\n");
    }
};

Child1 Child1::DEFAULT;

class Child2 {
    int x;
public:
    Child2(){
        x = 0;
        printf("Child2()\n");
    }

    Child2(Child2 &other){
        x = other.x;
        printf("Child2(Child2&)\n");
    }
};

class Parent {
    int x;
    Child1 c1;
    Child2 c2;

public:
    Parent(){
        printf("Parent()\n");
    }

    Parent(Parent &other) : c1(Child1::DEFAULT) {
        printf("Parent(Parent&)\n");
    }
};

int main(){
    printf("-----\n");
    Parent p1;
    printf("-----\n");
    Parent p2(p1);

    return 0;
}

Comments

1

When the compiler provides the default cctor, what do you think the compiler does for the member variables? It copy constructs it.

In the same vein, if the cctor is user-defined, and if one leaves out some members, those members cannot be left uninitialized. Class invariants are established during construction and have to be constantly maintained. So, the compiler does that for you.

4 Comments

-1 sorry. A compiler-provided default copy ctor will copy each member using that member's own copy ctor (which is a bitwise copy in the case of primitive types).
Yes, but i am saying the same thing! Default Initilalizes means copying via the member cctor.
I see what you're saying, but in fact the term "default initialises" has a specific, well-defined meaning in the C++ standard, which is to initialise an object with the type's default value (well, it's slightly more complicated but anyway...) So your description is a bit misleading.
@Abhay: If you change the phrase "default initialises" to "copy-constructs", I'll drop my -1.
1

Depending on how the base call constructor is initiated, the member's constructors will be called the same way. For example, let's start with:

struct ABC{
    int a;
    ABC() : a(0)    {   printf("Default Constructor Called %d\n", a);   };

    ABC(ABC  & other )  
    {
        a=other.a;
        printf("Copy constructor Called %d \n" , a ) ;
    };
};

struct ABCDaddy{
    ABC abcchild;
};

You can do these tests:

printf("\n\nTest two, where ABC is a member of another structure\n" );
ABCDaddy aD;
aD.abcchild.a=2;

printf( "\n Test: ABCDaddy bD=aD;  \n" );
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy cD(aD); \n" );
ABCDaddy cD(aD);    // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy eD; eD=aD;  \n" );
ABCDaddy eD;
eD=aD;          // Does NOT call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is not called)

Output:

Default Constructor Called 0

Test: ABCDaddy bD=aD;
Copy constructor Called 2

Test: ABCDaddy cD(aD);
Copy constructor Called 2

Test: ABCDaddy eD; eD=aD;
Default Constructor Called 0

Enjoy.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.