Constructor

In class-based object-oriented programming, a constructor is a special type of subroutine called to create an object.

Is a special kind of method that specifies one possible construction recipe for a new instance of the class.

In C++

This is how a object is actually created:

  1. Space is allocated
  2. Call the superclass constructor
  3. Initialize fields via a member initialization list (MIL)
  4. Run the constructor body

Compiler-Provided Constructor

If you do not write any constructors at all, you get a compiler provided default constuctor:

  • Primitive fields are uninitialized (garbage values)
  • Object fields are default constructed, see Initialization

If you write any constructor yourself, the compiler provided constructor is not supplied.

How does the compiler ACTUALLY know how much space to allocated?

This is related to the vptr question, that I messed up on the midterm.

It calls the parent class for example. And what if the parent class is actually a pure virtual function? Well it knows how much space the parent class needs, because there is a declaration on the methods and variables. Stackoverflow: https://stackoverflow.com/questions/36129281/c-how-does-the-compiler-know-how-much-memory-to-allocate-for-each-stack-frame

The constructor has the same name as the Class name.

IMPORTANT: Always Use Member Initialization Lists

Idiom to AVOID in C++

In Java and Python, in the constructor, we like to do: self.total = total or this->total = total but this is something to avoid in C++.

In C++, we have a cleaner way of doing things using Initializer.

There are cases where using the MIL is necessary

There are 4 cases where initialization certain types of fields in the constructor body does not work:

  1. Initializing const fields
  2. Initializing reference fields
  3. Object fields without default constructors
  4. If the superclass doesn’t have a default constructor
  1. Const fields
class Student {
	const int id;
	public:
		Student(int id) {
			this->id = id; // does NOT compile
		}
}
  1. Reference fields
class Student {
	University& myUni;
	public:
		Student(University& uni) {
			this->myUni = uni; // does NOT compile
		}
}
  1. Object fields without default constructors
class A {
	public:
		A(int x) {}
}
class B {
	A myA;
	public:
		B() {
			myA = A{5}; // does NOT compile, because A does not have a default constructor
		}
 
}
  1. If the superclass does not have a default constructor
class A {
	int x;
	public:
		A(int x): x{x} {}
}
 
class B: public A {
int y;
public:
	B(int x, int y) {
		this->x = x; // DOES NOT work
		this->y = y; 
	}
}

What if we gave A a default constructor?

class A {
	int x;
	public:
		A(int x): x{x} {}
}
 
class B: public A {
int y;
public:
	B(int x, int y) {
		// DOES NOT work, because x is private, not protected.
		this->x = x; 
		this->y = y; 
	}
}

Correct way:

class B: public A {
	int y;
	public: 
		B(int x, int y): A{x},y{y} {} 
}
  • This works even if A does not have a default constructor!

Constructor with Default Arguments

We don’t need to write different constructors for a different number of arguments. We can just use default parameters!

// Declaration
class Rational {
	int num, denom;
	public:
	Rational(int num=0, int denom=1): num {num}, denom{denom} {}
};
 
// Usage
Rational q;
Rational w{1};
Rational z{1,5};

Default parameters MUST be trailing

Something like Foo(string x="hello", int y) won’t compile because of ambiguity issues. imagine there is another constructor Foo(int z) and calling Foo{5}. We wouldn’t know which constructor to use.

Additionally, default parameters must only appear in the Declaration, not the Definition.

// Rational.h
class Rational {
	int num, denom;
	public:
	Rational(int num=0, int denom=1);
};
 
// Rational.cc (default constructors aren't provided)
#incldue "Rational.h"
Rational::Rational(int num, int denom): num{num}, denom{denom}{}
}

Example of Constructor and Inheritance

I also have this example in Inheritance, this is how constructors are done with inheritance.

class Shape {
	public:
		Shape(string name): name{name} {}
		virtual double getArea() {
			return 100;
 
		}
	protected:
		string name;
 
};
 
class Circle: public Shape {
	public:
		Circle(double radius, string name="circle"): Shape{name}, radius{radius} {}
		double getArea() {
			return PI * pow(radius, 2);
		}
 
	private:
		double radius;
};
 
class Rectangle: public Shape {
	public:
		Rectangle(string name="rectangle"): Shape{name} {}
		double getArea() {
			return 200;
 
		}
};

Constructor Overloading

This is when we have multiple constructor definitions, which differ in the parameters they take.

Balloon::Balloon() : colour {"Lyons hunting tartan"} {} 
Balloon::Balloon(string colour) : colour {colour} {}

In Python

The constructor is called

def __init__(self):
	...