views:

882

answers:

7

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()?
}
+3  A: 

Yes. Ctors are ctors.

Charlie Martin
+1, you beat me by 2 seconds. :)
John Dibling
-1: What does "Yes" mean as an answer to a question containing an "or"?
rstevens
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 :)
GMan
+8  A: 

Basically to expand Charlie's answer:

When you enter the body of a class constructor, every member of that class has to have been constructed. So when you hit {, you are guaranteed that all your members have been constructed.

And this means by calling their default constructor, unless of course you explicitly call another one.


Information / Edit

You didn't mark your edit, but I think it was the clarification on if it calls b's copy-constructor if not specified. The answer is no, it does do the default constructor, if a manual copy constructor is defined at all.

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

#include <iostream>

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

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

class Bar
{
public:
    Bar(void)
    {
     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(void)
    {
     std::cout << "In Baz::Baz()" << std::endl;
    }

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

int main(void)
{
    Baz baz1;
    Baz baz2(baz1);

    return 0;
}

This as-is (defined copy-constructor) prints this:

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

So left as is, it calls default on both.


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()
In Foo::Foo(const Foo&)
In Bar::Bar(const Bar&)

That is, it calls the copy-constructor on both. So as you can see, once you explicitly declare a copy-constructor you are responsible for the copying of all class members; otherwise they will be default constructed.


To finish up, only explicitly call foo's copy-constructor, like:

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

And you will get this:

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

So it will still only default-construct your members.


Conclusion

When explicitly defining a copy-constructor, the compiler will no longer automatically copy any members for you.

GMan
+2  A: 

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.

sharptooth
+1  A: 

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.

Abhay
-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).
j_random_hacker
Yes, but i am saying the same thing! *Default Initilalizes* means copying via the member cctor.
Abhay
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.
j_random_hacker
@Abhay: If you change the phrase "default initialises" to "copy-constructs", I'll drop my -1.
j_random_hacker
+1  A: 

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.

Smashery
+1  A: 

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;
}
Vulcan Eager
And the stdout was?
Darren Clark
A: 

For details see: http://stackoverflow.com/questions/563221/is-there-an-implicit-default-constructor-in-c/563320#563320

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.
Martin York