Skip to main content
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Special member functions
- Article
- 08/03/2021
- 2 minutes to read
In this article
The special member functions are class [or struct] member functions that, in certain cases, the compiler automatically generates for you. These functions are the default constructor, the destructor, the copy constructor and copy assignment operator, and the move constructor and move assignment operator. If your class does not define one or more of the special member functions, then the compiler may implicitly declare and define the functions that are used. The compiler-generated implementations are called the default special member functions. The compiler does not generate functions if they are not needed.
You can explicitly declare a default special member function by using the = default keyword. This causes the compiler to define the function only if needed, in the same way as if the function was not declared at all.
In some cases, the compiler may generate deleted special member functions, which are not defined and therefore not callable. This can happen in cases where a call to a particular special member function on a class doesn't make sense, given other properties of the class. To explicitly prevent automatic generation of a special member function, you can declare it as deleted by using the = delete keyword.
The compiler generates a default constructor, a constructor that takes no arguments, only when you have not declared any other constructor. If you have declared only a constructor that takes parameters, code that attempts to call a default constructor causes the compiler to produce an error message. The compiler-generated default constructor performs simple member-wise default initialization of the object. Default initialization leaves all member variables in an indeterminate state.
The default destructor performs member-wise destruction of the object. It is virtual only if a base class destructor is virtual.
The default copy and move construction and assignment operations perform member-wise bit-pattern copies or moves of non-static data members. Move operations are only generated when no destructor or move or copy operations are declared. A default copy constructor is only generated when no copy constructor is declared. It is implicitly deleted if a move operation is declared. A default copy assignment operator is generated only when no copy assignment operator is explicitly declared. It is implicitly deleted if a move operation is declared.
See also
C++ Language Reference
Feedback
Submit and view feedback for
Constructors are, first and foremost, just functions. They can be simple, complex, or anything in between. However, constructors are special functions that are called automatically whenever a new object is created [i.e., instantiated]. The primary purpose of constructors is to construct or initialize an object. Visually, constructors are set apart from "regular" functions by two characteristics: [a] they have the same name as the class for which they are constructing objects, and [b] they do not have a return type.
Initializer lists are used with constructors to initialize [i.e., assign the first or initial value to] an object's member variables. Before the ANSI 2014 standard, initializer lists were the only way to initialize member variables, and they remain the preferred way to initialize data members with values passed into the constructor as arguments.
Constructors
As you might guess, constructors are a pretty important part of object-oriented programs. Constructors are essential enough that five different kinds are independently named to make them easier to talk about.
- Default
- Conversion
- Copy
- Move
- General
class Foo { public: Foo[]; Foo[int x]; Foo[int x, int y]; }; | Foo f1; // [a] Foo* f2 = new Foo; //Foo f1[]; //Foo* f2 = new Foo[]; Foo f3[5]; // [b] Foo* f4 = new Foo[5]; Foo f5[5, 10]; // [c] Foo* f6 = new Foo[5, 10]; |
- Calls the default, no-argument constructor. Historically, a call to the default constructor did not allow parentheses, but a recent change to the ANSI C++ standard now permits them
- Calls the one-argument constructor
- Calls the two-argument constructor
Each constructor is designed to fill a specific programming need, but most classes will only need a few constructors - not all. The following sections describe each kind of constructor in detail, but the focus is on the constructor's visible operations. One object-oriented feature, polymorphism, requires that each object store a hidden pointer [called the vptr or virtual pointer]. One of the tasks of every constructor is to initialize the vptr pointer, which it does by running code that the compiler automatically inserts into each constructor. If the class does not have any constructors, the compiler creates a simple default constructor to initialize the vptr.
The Default Constructor
The primary characteristic that sets a default constructor apart from the other constructors is that it does not have any parameters. A default constructor is often used to create an "empty" object or an object initialized with default values. Although the textbook didn't state it at the time, many of our previous examples have relied on the string class default constructor. The string default constructor creates a string object that does not contain any characters. We can also use a default constructor to create an "empty" instance of our Time class:
class Time { private: int hours; int minutes; int seconds; }; | class Time { private: int hours = 0; int minutes = 0; int seconds = 0; }; |
[a] | [b] |
Time[] : hours[0], minutes[0], seconds[0] {} Or Time[] { hours = 0; minutes = 0; seconds = 0; } | Time t1; Time* t2 = new Time; Time t3[]; Time* t4 = new Time[]; |
[c] | [d] |
- For much of C++'s life, initializing data members in the class specification was not allowed.
- The ANSI 2014 standard allows member initialization in the class specification, which eases object instantiation.
- Data member initialization with an initializer list [preferred] and data member initialization in the function body [easier to understand].
- Different ways of calling the default constructor: with [t3 and t4] and without [t1 and t2] parentheses.
Conversion Constructor
A conversion constructor converts one data type into an instance of the class in which the constructor appears. What the conversion means and how the conversion function works depends very much on the source and destination types. A conversion constructor has one argument and the one highlighted in the following example converts an int into an instance of the Time class:
class Time { private: int hours; int minutes; int seconds; public: Time[int s] { hours = s / 3600; s %= 3600; minutes = s / 60; seconds = s % 60; } }; A conversion constructor. This constructor converts an integer into an instance of Time [i.e., into a Time object]The conversion constructor illustrated above is the second make_time function from the struct Time example rewritten as a constructor. It is possible to generalize the pattern of a conversion constructor as follows:
class Foo { . . . public: Foo[Bar b]; // converts a Bar into a Foo };Where Bar may be the name of a primitive, built-in data type, or it may be the name of a class. Here is another example of a conversion constructor based on strings and C-strings:
The definition of variable s converts a C-string ["Hello, World!"] into the string object s.
Copy Constructor
A copy constructor creates a new object by copying an existing object. C++ bases two critical and fundamental programming operations on the copy constructor:
- Pass by value
- Return by value
This means that whenever functions pass or return objects by value, the program copies the objects from one part of the program to another. Each operation creates a new object, and constructing new objects is always the task of a constructor function. These operations are so fundamental to programming that the compiler automatically generates a copy constructor for every class. In the situations that we've seen so far, the task of copying an existing object is easy enough that the compiler can create the constructor. Later, we will see more complex situations where the compiler-generated copy constructor is insufficient, and, in such cases, we must override it with our own constructor.
Since the copy constructor implements pass-by-value, the argument to the copy constructor cannot be passed by value [which would cause infinite recursion]. For this reason, copy constructors have a particular signature that makes them easy to identify and which programmers must follow when they need to override the compiler-generated copy constructor. Copy constructors always take a single argument that is the same class type as the class in which the constructor appears, and the argument is always a reference variable:
ClassName[const ClassName& o];class Person { private: string name; int weight; double height; public: Person[const Person& p]; }; | Person::Person[const Person& p] { name = p.name; weight = p.weight; height = p.height; } |
If one or more member variables is a pointer, the copy operation becomes more complex, and we defer dealing with this situation until the next chapter. For the curious or those facing a more immediate problem, please see The Copy Constructor in the next chapter.
fraction fraction::add[fraction f2] { fraction temp; temp.numerator = . . .; temp.denominator = . . .; return temp; } | fraction fraction::add[fraction f2] { int d = . . .; int n = . . .; return fraction[n, d]; } |
[a] | [b] |
- This version of the add function causes three implicit or "hidden" function calls. The first implicit call is to the default constructor [highlighted in yellow]. The return statement [highlighted in orange] calls the copy constructor to return the object and a destructor to destroy temp.
- The second version requires a general or parameterized constructor but saves two function calls. The object created in the return statement is constructed in the calling scope rather than in the function's scope. So, this version does not make a destructor call and only makes one general constructor call [yellow].
Move Constructor
Like the copy constructor, the move constructor can be identified by its distinctive argument list:
ClassName[ClassName&& o];If a move constructor has additional arguments, they must have default values [i.e., default arguments]. Unlike copy constructors, move constructors can take some or all the resources held by the argument object rather than copying them. The argument object remains in a valid but potentially incomplete state. Our study of the move constructor will extend only to recognizing and identifying it. The double ampersand, &&, which denotes an r-value reference declarator, and the move constructor are otherwise beyond the scope of CS 1410, but you'll study them in detail in CS 2420.
General Constructor
No special syntax or pattern defines a general constructor. A general constructor simply does not fit into one of the previously described categories above. So, any constructor that has two or more parameters is a general constructor just because it's not [a] a default constructor [no parameters], [b] a conversion constructor [has one parameter that's not a reference], or [c] a copy constructor [one parameter that is a reference]. It is possible to convert the first make_time function from the struct Time example into a general constructor:
Time::Time[int h, int m, int s] { hours = h; minutes = m; seconds = s; } A general constructor. If it's not a default constructor, a conversion constructor, a copy constructor, or a move constructor, then it's a general constructorInitializer List Notation
One common task of constructor functions is initializing an object's member variables regardless of the constructor's overall complexity. Although member initialization can occur in the constructor's body, it is considered best practice to initialize members with an initializer list. An initializer list has the advantage of running before the function's body. So, the member variables are ready to use as soon as the constructor body runs. Initializer lists begin with a colon and appear between a function's parameter list and the body's opening brace. Initializer lists follow a simple pattern:
class fraction { private: int numerator; int denominator; public: fraction[int n, int d] : numerator[n], denominator[d] {} }; Argument list notation. An argument list includes everything from the colon to [but not including] the opening brace of the empty function body. The symbols used in the initializer list are specific. The color coding shows the connection between the data members, the function arguments, and the symbols appearing in the initializer list.Constructors are the only functions that may have an initializer list, and the list is a part of the constructor's definition. So, if the function is prototyped in the class but defined elsewhere, the initializer list appears with the definition.
An initializer list is a comma-separated list of initializer elements. Each element behaves like an assignment, so numerator[n] is equivalent to numerator = n. The color coding in the figure above highlights how each initializer element is formed: the first part of each element is the name of a member variable and the second part [enclosed in parentheses] is the name of one of the function's parameters. With one exception, the list elements may appear in any order, but a list element that initializes an inheritance relationship [i.e., calls a superclass constructor], must appear first in the list.
fraction::fraction[int n, int d] { numerator = n; denominator = d; int common = gcd[numerator, denominator]; numerator /= common; denominator /= common; } | fraction::fraction[int n, int d] : numerator[n], denominator[d] { int common = gcd[numerator, denominator]; numerator /= common; denominator /= common; } |
[a] | [b] |
- The constructor body has one assignment operation for each member variable and each constructor argument. I've deliberately written the function to emphasize the difference between the two illustrated techniques, but in fairness, we can simplify this version: int common = gcd[n, d]; numerator = n / common; denominator = d / common;
- An initializer list [highlighted] behaves like a series of assignment operations but is preferred to the explicit assignment because it takes place before any statements in the constructor body run
- The variable p is an instance of class Person; when a class is instantiated, a constructor function is called automatically and the arguments, Dilbert, 5.9, and 150, are passed to the constructor's parameters
- The data from the constructor call is passed into the function's parameters, which are variables named a_name, a_height, and a_weight
- The variable names in the function parameter list are used in the initializer list that appears on the right side of the colon
- Each element of the initializer list consists of a member variable name followed, in parentheses, by the name of a constructor parameter
Every function must have exactly one body. The body is often empty in the case of very simple constructors whose only purpose is to initialize the object's member variables. In the following example, the {} at the end is the function's empty body and not part of the initializer list.
Initializer lists are a part of the function definition and not of the declaration or prototype. So, if the class only contains a function prototype and the function definition is in a separate .cpp file, then the initializer list goes with the function definition in the .cpp file:
class fraction { private: int numerator; int denominator; public: fraction[int n, int d]; }; | fraction::fraction[int n, int d] : numerator[n], denominator[d] { int common = gcd[numerator, denominator]; numerator /= common; denominator /= common; } |
Caution:
class fraction { public: fraction[int n, int d] {} }; | fraction::fraction[int n, int d] : numerator[n], denominator[d] { . . . . } |
Default Arguments
Although the UML has always permitted class designers to specify initial values for both member variables and function arguments, C++ originally did not allow programmers to initialize member variables in the class specification. So, programmers initialized member variables with constructors, and you may still see examples of this in existing code. However, C++ has always supported default arguments, which may be used with any C++ function [not just constructors]. When we use default arguments with constructors, they must follow all of the rules listed in chapter 6 [and it's probably a good idea to review those rules now].
+fraction[n: int = 0, d : int = 1] | fraction[int n = 0, int d = 1]; |
[a] | [b] |
- The UML constructor
- is translated into the C++ code
In "real world" C++ programs, it is common for the class specification to appear in a .h file and the member functions [including constructors] to appear in a .cpp file. When we follow this organization, there is one unfortunate aspect of initializer lists and constructor default arguments that we must memorize:
class fraction { private: int numerator; int denominator; public: fraction[int n = 0, int d = 1]; }; | fraction::fraction[int n, int d] : numerator[n], denominator[d] { int common = gcd[numerator, denominator]; numerator /= common; denominator /= common; } |
[a] | [b] |
- Default arguments
appear with the function PROTOTYPE in the header.[i.e., .h] file. This constructor may be called in one of three ways:
- fraction[]; // acts as a default constructor
- fraction[n]; // acts as a conversion constructor
- fraction[n,d];
- Initializer lists appear with the function DEFINITION in the source code [i.e., .cpp] file.
Default Constructors and In-Class Initialization
If a class only needs a constructor to initialize the member variables, replacing the constructor with initializations directly in the class specification is appropriate. The compiler will automatically create a default constructor to initialize the vptr as needed. However, initializing member variables directly inside the class specification does not take the place of a default constructor or default arguments when object construction requires operations more complex than member initialization. Furthermore, if the class defines one or more parameterized constructors, then a default constructor or default arguments are still needed if the programmer wishes to create an object without calling a parameterized constructor:
fraction f1; fraction* f2 = new fraction;Constructor Chaining
Like any function, constructors can range from algorithmically simple to complex. Sometimes complex constructors perform the same operations as simple ones, followed by additional operations befitting their complex nature. We can often avoid the overhead of writing and maintaining duplicate constructor code by putting the common, often simple, code in a basic constructor and allowing more advanced constructors to call the basic one. Java has always supported in-class constructor chaining by using this[...] as the name of an overloaded constructor and the number and type of arguments to differentiate between them. Before the adoption of the C++ 2011 standard, C++ did not permit in-class constructor chaining, but it does now, albeit with limitations.
class Table { private: int rows; int cols; int** array; }; | Table[int r, int c] : rows[r], cols[c] { array = new int*[rows]; for [int i = 0; i < rows; i++] array[i] = new int[cols]; } |
[a] | [b] |
Table[const Table& t] : Table[trows, cols] { for [int i = 0; i < rows; i++] for [int j = 0; j < cols; j++] array[i][j] = array[i][j]; } | Table[const Table& t] : Table[trows, cols], member[10] { . . } |
[c] | [d] |
- A class that implements a table as a two-dimensional array. rows and cols are the number of rows and columns in the table, respectively. array is a pointer to an array of pointers that make up the table rows.
- A "simple" constructor that initializes the data members. The for-loop one table row during each iteration. Note that rows and cols are initialized before the statements in the body of the constructor run.
- Delegation describes the situation where one member function calls another - the caller delegates some of its responsibility to the called function. This example illustrates the copy constructor implemented as a delegation constructor - it calls the constructor illustrated in [b], highlighted in yellow, to create a new Table object and then copies the contents of this object to the new object. Any constructor can delegate to another, so "delegation constructor" is not included in the named constructors above.
- C++ imposes a severe restriction on delegation: it only allows one initializer in the initializer list. Assume for a moment that the Table class has a data member named member. Ordinarily, we could initialize member using the operation highlighted in coral. But doing this results in a list with two comma-separated initializers, and the highlighted code is an error.
Chaining constructors works well when the operations of the called or delegated constructor [e.g., [b]] must or can run before the operations in the calling or delegating constructor [e.g., [c]]. When that is not the case, the best we can do in C++ or Java is use a helper function to implement the common code.
display[] { ...; ...; } | Window[] { ...; display[]; } |
[a] | [b] |
Window[int x, int y] { ...; display[]; } | Window[int x, int y, int color] { ...; display[]; } |
[c] | [d] |
- A helper function named display that displays a Window object. Helpers are typically included in the class's private section so that a user cannot call them directly.
- Creates a simple Window, perhaps using default values. The constructor displays the window following its initialization.
- The user of the Window class specifies the coordinates of the upper left hand corner of the window [x and y] when creating the object and then displays it.
- In the final example, the constructor has three parameters: the location on the screen at [x,y] and the color of the window. Like example [c], the helper function displays the window when its initialization is complete.
1 A bitwise copy simply means that the computer copies a patch of bits, bit-for-bit, from one memory location to another. The copy can be completed with the memcpy function, which is supported on many computers with a single machine instruction [and so is very fast and efficient]. For example, given the following code fragment:
class foo { . . . }; foo f1; foo f2; f1 = f2;The compiler-generated copy constructor implements the assignment operation as:
memcpy[&f1, &f2, size of[foo]];Back | Chapter TOC | Next