The compiler is allowed to make one implicit conversion to resolve the parameters to a function. What this means is that the compiler can use constructors callable with a single parameter to convert from one type to another in order to get the right type for a parameter.
Here's an example class with a constructor that can be used for implicit conversions:
class Foo
{
public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo)
{
}
int GetFoo () { return m_foo; }
private:
int m_foo;
};
Here's a simple function that takes a Foo
object:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
and here's where the DoBar
function is called:
int main ()
{
DoBar (42);
}
The argument is not a Foo
object, but an int
. However, there exists a constructor for Foo
that takes an int
so this constructor can be used to convert the parameter to the correct type.
The compiler is allowed to do this once for each parameter.
Prefixing the explicit
keyword to the constructor prevents the compiler from using that constructor for implicit conversions. Adding it to the above class will create a compiler error at the function call DoBar (42)
. It is now necessary to call for conversion explicitly with DoBar (Foo (42))
The reason you might want to do this is to avoid accidental construction that can hide bugs.
Contrived example:
- You have a
MyString
class with a constructor that constructs a string of the given size. You have a function print(const MyString&)
(as well as an overload print (char *string)
), and you call print(3)
(when you actually intended to call print("3")
). You expect it to print "3", but it prints an empty string of length 3 instead.
Best Solution
Introduction
C++ treats variables of user-defined types with value semantics. This means that objects are implicitly copied in various contexts, and we should understand what "copying an object" actually means.
Let us consider a simple example:
(If you are puzzled by the
name(name), age(age)
part, this is called a member initializer list.)Special member functions
What does it mean to copy a
person
object? Themain
function shows two distinct copying scenarios. The initializationperson b(a);
is performed by the copy constructor. Its job is to construct a fresh object based on the state of an existing object. The assignmentb = a
is performed by the copy assignment operator. Its job is generally a little more complicated, because the target object is already in some valid state that needs to be dealt with.Since we declared neither the copy constructor nor the assignment operator (nor the destructor) ourselves, these are implicitly defined for us. Quote from the standard:
By default, copying an object means copying its members:
Implicit definitions
The implicitly-defined special member functions for
person
look like this:Memberwise copying is exactly what we want in this case:
name
andage
are copied, so we get a self-contained, independentperson
object. The implicitly-defined destructor is always empty. This is also fine in this case since we did not acquire any resources in the constructor. The members' destructors are implicitly called after theperson
destructor is finished:Managing resources
So when should we declare those special member functions explicitly? When our class manages a resource, that is, when an object of the class is responsible for that resource. That usually means the resource is acquired in the constructor (or passed into the constructor) and released in the destructor.
Let us go back in time to pre-standard C++. There was no such thing as
std::string
, and programmers were in love with pointers. Theperson
class might have looked like this:Even today, people still write classes in this style and get into trouble: "I pushed a person into a vector and now I get crazy memory errors!" Remember that by default, copying an object means copying its members, but copying the
name
member merely copies a pointer, not the character array it points to! This has several unpleasant effects:a
can be observed viab
.b
is destroyed,a.name
is a dangling pointer.a
is destroyed, deleting the dangling pointer yields undefined behavior.name
pointed to before the assignment, sooner or later you will get memory leaks all over the place.Explicit definitions
Since memberwise copying does not have the desired effect, we must define the copy constructor and the copy assignment operator explicitly to make deep copies of the character array:
Note the difference between initialization and assignment: we must tear down the old state before assigning to
name
to prevent memory leaks. Also, we have to protect against self-assignment of the formx = x
. Without that check,delete[] name
would delete the array containing the source string, because when you writex = x
, boththis->name
andthat.name
contain the same pointer.Exception safety
Unfortunately, this solution will fail if
new char[...]
throws an exception due to memory exhaustion. One possible solution is to introduce a local variable and reorder the statements:This also takes care of self-assignment without an explicit check. An even more robust solution to this problem is the copy-and-swap idiom, but I will not go into the details of exception safety here. I only mentioned exceptions to make the following point: Writing classes that manage resources is hard.
Noncopyable resources
Some resources cannot or should not be copied, such as file handles or mutexes. In that case, simply declare the copy constructor and copy assignment operator as
private
without giving a definition:Alternatively, you can inherit from
boost::noncopyable
or declare them as deleted (in C++11 and above):The rule of three
Sometimes you need to implement a class that manages a resource. (Never manage multiple resources in a single class, this will only lead to pain.) In that case, remember the rule of three:
(Unfortunately, this "rule" is not enforced by the C++ standard or any compiler I am aware of.)
The rule of five
From C++11 on, an object has 2 extra special member functions: the move constructor and move assignment. The rule of five states to implement these functions as well.
An example with the signatures:
The rule of zero
The rule of 3/5 is also referred to as the rule of 0/3/5. The zero part of the rule states that you are allowed to not write any of the special member functions when creating your class.
Advice
Most of the time, you do not need to manage a resource yourself, because an existing class such as
std::string
already does it for you. Just compare the simple code using astd::string
member to the convoluted and error-prone alternative using achar*
and you should be convinced. As long as you stay away from raw pointer members, the rule of three is unlikely to concern your own code.