CPSC 427: Object-Oriented Programming
Michael J. Fischer
Construction, Initialization, and Destruction
Initializing
an
object
Whenever a class object is created, one of its constructors is called.
This applies not only to the “outer” object but also to all of its
embedded objects.
If not specified otherwise, the default constructor is called, if defined. This is the one that takes no arguments.
Example: MyClass mc; calls default constructor mc.
If you do not define any constructors, then the default constructor is defined automatically to be the null constructor.
Which constructor gets used?
A class can have several constructor methods, differing from each other
in the number and types of arguments.
When an object is created, the constructor called is the one matching
the user-specified arguments.
For example, suppose the user declares two Parallelogram objects:
Parallelogram tempShape;
Parallelogram yellowShape( 5, 5, 30 );
tempShape is initialized by calling the null constructor.
yellowShape is initialized by calling Parallelogram(5, 5, 30).
Construction rules for a simple class
The rule for constructing an object of a simple class is:
Construction rules for a derived class
The rule for constructing an object of a derived class is:
Destruction rules
When an object is deleted, the destructors are called in the opposite
order before the storage allocated to the object is released back to the
system.
The rule for an object of a derived class is:
Rules for a simple class are the same except that step 3 is omitted.
Constructor ctors
Ctors (short for constructor/initializors) allow one to supply parameters
to implicitly-called constructors.
Example:
Initialization ctors
Ctors also can be used to initialze primitive (non-class) variables.
Example:
Multiple ctors are separated by commas.
Ctors present must be in the same order as the construction takes place – base class ctor first, then data member ctors in the same order as their declarations in the class.
Initialization not same as assignment
Previous example using ctors is not the same as writing
Deriv( int n ) { y=n+1; x=n; };
Special member functions
A class has six special member functions. These are special because they
are defined automatically if the programmer does not redefine them.
They are distinguished by their prototypes.
Name | Prototype |
Default constructor | MyClass(); |
Destructor | ~MyClass(); |
Copy constructor | MyClass( const MyClass& other ); |
Move constructor | MyClass( MyClass&& other ); |
Copy assignment | MyClass& operator=( const T& other ); |
Move assignment | MyClass& operator=( T&& other ); |
Special function automatic definitions
Name | Automatic Definition |
Default constructor | Null constructor does nothing; |
Destructor | Function that does nothing |
Copy constructor | Does a shallow copy from its argument |
Move constructor | (later) |
Copy assignment | Does a shallow copy from rhs to lhs |
Move assignment | (later) |
Copy and assignment have the same default semantics but can be redefined to behave differently.
Deletion
Some of the automatic definitions are omitted if certain special functions
are defined by the user.
For example, if you define a constuctor with arguments, then the default
constructor is automatically deleted.
You can explicitly remove any automatically-created special function by
using =delete in place of a definition.
Example: To remove the copy constructor for MyClass, write
MyClass( const MyClass& ) = delete;
Restoration of automatically deleted definition
If a default definition for a special function is automatically deleted, it
can be brought back using =default in place of a definition.
For example, if you define a constuctor with arguments, then the default
constructor is automatically deleted.
To bring it back, you can write MyClass() = default;.
Copy constructors
Move constructors
C++11 introduced a move constructor. Its purpose is to allow an object
to be safely moved from one variable to another while avoiding the
“double delete” problem.
We’ll return to this interesting topic later, after we’ve looked more closely at dynamic extensions.
Reference Types
Reference types Recall: Given int x, two types are associated with x: an L-value (the reference to x) and an R-value (the type of its values).
C++ exposes this distinction through reference types and declarators.
A reference type is any type T followed by &, i.e., T&.
A reference type is the internal type of an L-value.
Example: Given int x, the name x is bound to an L-value of type int&, whereas the values stored in x have type int
This generalizes to arbitrary types T: If an L-value stores values of type T, then the type of the L-value is T&.
Reference declarators
The syntax T& can be used to declare names, but its meaning is not what one might expect.
int x = 3; | // Ordinary int variable |
int& y = x; | // y is an alias for x |
y = 4; | // Now x == 4. |
The declaration must include an initializer.
The meaning of int& y = x; is that y becomes a name for the L-value x.
Since x is simply the name of an L-value, the effect is to make y an alias for x.
For this to work, the L-value type (int&) of x must match the type declarator (int&) for y, as above.
Use of named references
Named references can be used just like any other variable.
One application is to give names to otherwise unnamed objects.
Reference parameters
References are mainly useful for function parameters and return values.
When used to declare a function parameter, they provide call-by-reference semantics.
int f( int& x ){…}
Within the body of f, x is an alias for the actual parameter, which must be the L-value of an int location.
Reference return values
Functions can also return references.
This code returns a reference to the smaller of x and y and then sets that variable to their sum.