+5  A: 

The compiler provides a copy constructor unless you declare (note: not define) one yourself. The compiler-generated copy constructor simply calls the copy constructor of each member of the class (and of each base class).

The very same is true for the assignment operator and the destructor, BTW. It is different for the default constructor, though: That is provided by the compiler only if you do not declare any other constructor yourself.

sbi
+1  A: 

Yes, the compiler-generated copy constructor performs a member-wise copy, in the order in which the members are declared in the containing class. If any of the member types do not themselves offer a copy constructor, the would-be copy constructor of the containing class cannot be generated. It may still be possible to write one manually, if you can decide on some appropriate means to initialize the value of the member that can't be copy-constructed -- perhaps by using one of its other constructors.

seh
A: 

The compiler will generate the needed constructors for you.

However, as soon as you define a copy-constructor yourself, the compiler gives up generating anything for that class and will give and error if you don't have the appropriate constructors defined.

Using your example:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Trying to default instantiate or copy-construct Foo will throw an error since Baz is not copy-constructable and the compiler can't generate the default and copy constroctor for Foo.

Coincoin
Does this apply to any constructor? If I define a no-arg constructor, will the compiler still generate any constructors?
Jergason
My bad, you are right, default doesn't prevent copy, it's the other way around.
Coincoin
Be careful how you use the word "Throw" that implies a runtime error. Problems with copy constructors (not being copy-constructable) are detected at compile time.
Martin York
A: 

The C++ default copy constructor creates a shallow copy. A shallow copy will not create new copies of objects that your original object may reference; the old and new objects will simply contain distinct pointers to the same memory location.

Phil
I know it creates a shallow copy and objects that are pointed to will not be copied, but what about objects that are simply contained, like my example?
Jergason
Oops. Sorry, I've been living in Java land too long, and forgot that in C++ objects can go on the stack.
Phil
It shallow copies pointers. Objects are fully copied.
Martin York
Well, I prefer to say that it copies by value, and pointers are values, so just the pointer itself gets copied as a value. The object pointed to by the pointer is not copied. Doing so would create a new object with a new address, which would necessitate a different value in the resulting pointer, which definitely doesn't seem like a "copied pointer".
seh
+7  A: 
Foo f1;
Foo f2(f1);

Yes this will do what you expect it to:
The f2 copy constructor Foo::Foo(Foo const&) is called.
This copy constructs its base class and then each member (recursively)

If you define a class like this:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

The following methods will be defined by your compiler.

  • Constructor (default)
  • Constructor (Copy)
  • Destructor (default)
  • Assignment operator

Constructor: Default:

X::X()
    :Y()                // Calls the base constructor
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
{
}

Notes: If the base class or any members do not have a valid visible default constructor then the default constructor can not be generated. This is not an error unless your code tries to use the default constructor (then only a compile time error).

Constructor (Copy)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

Notes: If the base class or any members do not have a valid visible copy constructor then the copy constructor can not be generated. This is not an error unless your code tries to use the copy constructor (then only a compile time error).

Assignment Operator

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

Notes: If the base class or any members do not have a valid viable assignment operator then the assignment operator can not be generated. This is not an error unless your code tries to use the assignment operator (then only a compile time error).

Destructor

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • If any constructor (including copy) is declared then the default constructor is not implemented by the compiler.
  • If the copy constructor is declared then the compiler will not generate one.
  • If the assignment operator is declared then the compiler will not generate one.
  • If a destructor is declared the compiler will not generate one.

Looking at your code the following copy constructors are generated:

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}
Martin York
`m_a`, `m_b`, and `m_c` aren't very informative names. This wouldn't be a problem, except that you initially define them as `m_a`, `m_c` (for the `char *`), and `m_d` (for the `Z` type). I suspect that more informative names would have avoided this minor error. +1 anyway for a good post.
Chris Lutz
Fixed Type: The names are like that deliberately so that order can be shown. I would have used m_1,m_2,m_3 but I don't like numbers in identifiers.
Martin York