Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++
Prev Contents / Index Next

Default copy-constructor

Because the copy-constructor implements pass and return by value, it’s important that the compiler creates one for you in the case of simple structures – effectively, the same thing it does in C. However, all you’ve seen so far is the default primitive behavior: a bitcopy.

When more complex types are involved, the C++ compiler will still automatically create a copy-constructor if you don’t make one. Again, however, a bitcopy doesn’t make sense, because it doesn’t necessarily implement the proper meaning.

Here’s an example to show the more intelligent approach the compiler takes. Suppose you create a new class composed of objects of several existing classes. This is called, appropriately enough, composition, and it’s one of the ways you can make new classes from existing classes. Now take the role of a naive user who’s trying to solve a problem quickly by creating a new class this way. You don’t know about copy-constructors, so you don’t create one. The example demonstrates what the compiler does while creating the default copy-constructor for your new class:

//: C11:DefaultCopyConstructor.cpp
// Automatic creation of the copy-constructor
#include <iostream>
#include <string>
using namespace std;

class WithCC { // With copy-constructor
public:
  // Explicit default constructor required:
  WithCC() {}
  WithCC(const WithCC&) {
    cout << "WithCC(WithCC&)" << endl;
  }
};

class WoCC { // Without copy-constructor
  string id;
public:
  WoCC(const string& ident = "") : id(ident) {}
  void print(const string& msg = "") const {
    if(msg.size() != 0) cout << msg << ": ";
    cout << id << endl;
  }
};

class Composite {
  WithCC withcc; // Embedded objects
  WoCC wocc;
public:
  Composite() : wocc("Composite()") {}
  void print(const string& msg = "") const {
    wocc.print(msg);
  }
};

int main() {
  Composite c;
  c.print("Contents of c");
  cout << "Calling Composite copy-constructor"
       << endl;
  Composite c2 = c;  // Calls copy-constructor
  c2.print("Contents of c2");
} ///:~

The class WithCC contains a copy-constructor, which simply announces that it has been called, and this brings up an interesting issue. In the class Composite, an object of WithCC is created using a default constructor. If there were no constructors at all in WithCC, the compiler would automatically create a default constructor, which would do nothing in this case. However, if you add a copy-constructor, you’ve told the compiler you’re going to handle constructor creation, so it no longer creates a default constructor for you and will complain unless you explicitly create a default constructor as was done for WithCC.

The class WoCC has no copy-constructor, but its constructor will store a message in an internal string that can be printed out using print( ). This constructor is explicitly called in Composite’s constructor initializer list (briefly introduced in Chapter 8 and covered fully in Chapter 14). The reason for this becomes apparent later.

The class Composite has member objects of both WithCC and WoCC (note the embedded object wocc is initialized in the constructor-initializer list, as it must be), and no explicitly defined copy-constructor. However, in main( ) an object is created using the copy-constructor in the definition:

Composite c2 = c;

The copy-constructor for Composite is created automatically by the compiler, and the output of the program reveals the way that it is created:

Contents of c: Composite()
Calling Composite copy-constructor
WithCC(WithCC&)
Contents of c2: Composite()

To create a copy-constructor for a class that uses composition (and inheritance, which is introduced in Chapter 14), the compiler recursively calls the copy-constructors for all the member objects and base classes. That is, if the member object also contains another object, its copy-constructor is also called. So in this case, the compiler calls the copy-constructor for WithCC. The output shows this constructor being called. Because WoCC has no copy-constructor, the compiler creates one for it that just performs a bitcopy, and calls that inside the Composite copy-constructor. The call to Composite::print( ) in main shows that this happens because the contents of c2.wocc are identical to the contents of c.wocc. The process the compiler goes through to synthesize a copy-constructor is called memberwise initialization.

It’s always best to create your own copy-constructor instead of letting the compiler do it for you. This guarantees that it will be under your control.

Thinking in C++
Prev Contents / Index Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire