What constructor is used here? Not the default constructor, and not the constructor with a const char * parameter. Remember, initialization using this form is another syntax for the following:
StringBad sailor = StringBad(sports); //constructor using sports
Because sports is type StringBad, a matching constructor could have this prototype:
StringBad(const StringBad &);
And it turns out that the compiler automatically generates this constructor (called a
Special Member Functions
The problems with the StringBad class stem from
• A default constructor if you define no constructors
• A default destructor if you don’t define one
• A copy constructor if you don’t define one
• An assignment operator if you don’t define one
• An address operator if you don’t define one
More precisely, the compiler generates definitions for the last three items if a program uses objects in such a way as to require them. For example, if you assign one object to another, the program provides a definition for the assignment operator.
It turns out that the implicit copy constructor and the implicit assignment operator cause the StringBad class problems.
The implicit address operator returns the address of the invoking object (that is, the value of the this pointer). That’s fine for our purposes, and we won’t discuss this member function further. The default destructor does nothing, and we won’t discuss it, either, other than to point out that the class has already provided a substitute for it. But the others do warrant more discussion.
C++11 provides two more special member functions—the
Default Constructors
If you fail to provide any constructors at all, C++ provides you with a default constructor. For example, suppose you define a Klunk class and omit any constructors. In this case, the compiler supplies the following default:
Klunk::Klunk() { } // implicit default constructor
That is, it supplies a constructor (the
Klunk lunk; // invokes default constructor
The default constructor makes lunk like an ordinary automatic variable; that is, its value at initialization is unknown.
After you define any constructor, C++ doesn’t bother to define a default constructor. If you want to create objects that aren’t initialized explicitly, you then have to define a default constructor explicitly. It’s a constructor with no arguments, but you can use it to set particular values:
Klunk::Klunk() // explicit default constructor
{
klunk_ct = 0;
...
}
A constructor with arguments still can be a default constructor if all its arguments have default values. For example, the Klunk class could have the following inline constructor:
Klunk(int n = 0) { klunk_ct = n; }
However, you can have only one default constructor. That is, you can’t do this:
Klunk() { klunk_ct = 0 } // constructor #1
Klunk(int n = 0) { klunk_ct = n; } // ambiguous constructor #2
Why is this ambiguous? Consider the following two declarations:
Klunk kar(10); // clearly matches Klunt(int n)
Klunk bus; // could match either constructor
The second declaration matches constructor #1 (no argument), but it also matches constructor #2 (using the default argument 0). This will cause the compiler to issue an error message.
Copy Constructors
A copy constructor is used to copy an object to a newly created object. That is, it’s used during initialization, including passing function arguments by value and not during ordinary assignment. A copy constructor for a class normally has this prototype:
Note that it takes a constant reference to a class object as its argument. For example, a copy constructor for the String class would have this prototype:
StringBad(const StringBad &);
You must know two things about a copy constructor: when it’s used and what it does.
When a Copy Constructor Is Used