Constructors
A constructor is a special kind of class member function that is executed when an object of that class is instantiated. Constructors are typically used to initialize member variables of the class to appropriate default values, or to allow the user to easily initialize those member variables to whatever values are desired.
Unlike normal functions, constructors have specific rules for how they must be named:
1) Constructors should always have the same name as the class (with the same capitalization)
2) Constructors have no return type (not even void)
A constructor that takes no parameters (or has all optional parameters) is called a default constructor.
Here is an example of a class that has a default constructor:
class Fraction
{
private:
int m_nNumerator;
int m_nDenominator;
public:
Fraction() // default constructor
{
m_nNumerator = 0;
m_nDenominator = 1;
}
int GetNumerator() { return m_nNumerator; }
int GetDenominator() { return m_nDenominator; }
double GetFraction() { return static_cast<double>(m_nNumerator) / m_nDenominator; }
};
This class was designed to hold a fractional value as an integer numerator and denominator. We have defined a default constructor named Fraction (the same as the class). When we create an instance of the Fraction class, this default constructor will be called immediately after memory is allocated, and our object will be initialized. For example, the following snippet:
Fraction cDefault; // calls Fraction() constructor std::cout << cDefault.GetNumerator() << "/" << cDefault.GetDenominator() << std::endl;
produces the output:
0/1
Note that our numerator and denominator were initialized with the values we set in our default constructor! This is such a useful feature that almost every class includes a default constructor. Without a default constructor, the numerator and denominator would have garbage values until we explicitly assigned them reasonable values.
Constructors with parameters
While the default constructor is great for ensuring our classes are initialized with reasonable default values, often times we want instances of our class to have specific values. Fortunately, constructors can also be declared with parameters. Here is an example of a constructor that takes two integer parameters that are used to initialize the numerator and denominator:
#include <cassert>
class Fraction
{
private:
int m_nNumerator;
int m_nDenominator;
public:
Fraction() // default constructor
{
m_nNumerator = 0;
m_nDenominator = 1;
}
// Constructor with parameters
Fraction(int nNumerator, int nDenominator=1)
{
assert(nDenominator != 0);
m_nNumerator = nNumerator;
m_nDenominator = nDenominator;
}
int GetNumerator() { return m_nNumerator; }
int GetDenominator() { return m_nDenominator; }
double GetFraction() { return static_cast<double>(m_nNumerator) / m_nDenominator; }
};
Note that we now have two constructors: a default constructor that will be called in the default case, and a second constructor that takes two parameters. These two constructors can coexist peacefully in the same class due to function overloading. In fact, you can define as many constructors as you want, so long as each has a unique signature (number and type of parameters).
So how do we use this constructor with parameters? It’s simple:
Fraction cFiveThirds(5, 3); // calls Fraction(int, int) constructor
This particular fraction will be initialized to the fraction 5/3!
Note that we have made use of a default value for the second parameter of the constructor with parameters, so the following is also legal:
Fraction Six(6); // calls Fraction(int, int) constructor
In this case, our default constructor is actually somewhat redundant. We could simplify this class as follows:
#include <cassert>
class Fraction
{
private:
int m_nNumerator;
int m_nDenominator;
public:
// Default constructor
Fraction(int nNumerator=0, int nDenominator=1)
{
assert(nDenominator != 0);
m_nNumerator = nNumerator;
m_nDenominator = nDenominator;
}
int GetNumerator() { return m_nNumerator; }
int GetDenominator() { return m_nDenominator; }
double GetFraction() { return static_cast<double>(m_nNumerator) / m_nDenominator; }
};
This constructor has been defined in a way that allows it to serve as both a default and a non-default constructor!
Fraction cDefault; // will call Fraction(0, 1) Fraction cSix(6); // will call Fraction(6, 1) Fraction cFiveThirds(5,3); // will call Fraction(5,3)
Classes without default constructors
What happens if we do not declare a default constructor and then instantiate our class? The answer is that C++ will allocate space for our class instance, but will not initialize the members of the class (similar to what happens when you declare an int, double, or other basic data type). For example:
class Date
{
private:
int m_nMonth;
int m_nDay;
int m_nYear;
};
int main()
{
Date cDate;
// cDate's member variables now contain garbage
// Who knows what date we'll get?
return 0;
}
In the above example, because we declared a Date object, but there is no default constructor, m_nMonth, m_nDay, and m_nYear were never initialized. Consequently, they will hold garbage values. Generally speaking, this is why providing a default constructor is almost always a good idea:
class Date
{
private:
int m_nMonth;
int m_nDay;
int m_nYear;
public:
Date(int nMonth=1, int nDay=1, int nYear=1970)
{
m_nMonth = nMonth;
m_nDay = nDay;
m_nYear = nYear;
}
};
int main()
{
Date cDate; // cDate is initialized to Jan 1st, 1970 instead of garbage
Date cToday(9, 5, 2007); // cToday is initialized to Sept 5th, 2007
return 0;
}
8.6 — Destructors
|
Index
|
8.4 — Access functions and encapsulation
|
8.6 — Destructors
Index
8.4 — Access functions and encapsulation
As a review you could include protecting m_nDenominator from invalid assumptions by making sure it is not set to 0. This is because, if I remember correctly, dividing anything by zero will probably cause the program to crash so the function GetFraction() would most likely crash the program if the denominator is 0.
Great point! I updated the example. Dividing by 0 will definitely crash the program.
what’s assert() ?
I talk about assert in section 7.12. In short, it’s a way to test whether an expression is true, and if not, stop the program at that point.
double GetFraction() { return static_cast(m_nNumerator)/nDenominator; }
“m_nNumerator)/nDenominator” should be “m_nNumerator)/m_nDenominator ”
at all three places in the tutorial.
Thanks,
Renu
class Fraction
{
private:
int m_nNumerator =10;
int m_nDenominator=20;
public:
Fraction() // default constructor …….
I tried initialising m_nNumerator =10 and int m_nDenominator=20
got this error ” only static const can be initialised ….”
Then I tried
private:
static const int m_nValue=22;
int m_nNumerator ;
int m_nDenominator;
It worked.
Could you please explain? Does that mean private member variable cannot be initialised when they are declared? If so ,is there any specific reason for that?
Thanks,
Renu
In a class, you can’t initialize values on the line they are declared like that. Non-static class values should be initialized in the constructor body or initialization list. Static class values should be initialized in the body of the class definition.
I am not sure what the reasoning is for this design decision. I presume simply because the constructor is the function that is supposed to initialize your values. If you were allowed to do default values this way, then many variables would be initialized twice (once in the declaration, once in the constructor). This would be both inefficient and confusing.
hi renu,
there is not specific reason why we can not initialize private data in class … It’s C++ basic rule that user shouldn’t initialize private,protected or public data inside class without using function or constructor…..
I believe this may be because the time you are declaring your classes, memory is not allocated to it. It’s merely a declaration of the class where you are trying to initialize the member variable whose memory hasn’t yet alloted.
Typo:
“Date cToday(9, 5, 2007); // cDate is initialized to Sept 5th, 2007 ”
Should be:
“Date cToday(9, 5, 2007); // cToday is initialized to Sept 5th, 2007 ”
[ Fixed. Thanks! -Alex ]
It’s really help full for students like for me .
I had recommended this to my fiends too.
Do constructors must have same name as class or can have different name?
thanks.
Constructors always have the same name as the class.
So I tried compiling a really simple example from above (constructors with parameters) using
#include <iostream> #include <cassert> using namespace std; class Fraction { private: int m_nNumerator; int m_nDenominator; public: Fraction() // default constructor { m_nNumerator = 0; m_nDenominator = 1; } // Constructor with parameters Fraction(int nNumerator, int nDenominator=1) { assert(nDenominator); m_nNumerator = nNumerator; m_nDenominator = nDenominator; } int GetNumerator() { return m_nNumerator; } int GetDenominator() { return m_nDenominator; } double GetFraction() { return static_cast<double>(m_nNumerator)/nDenominator; } }; int main() { return 0; }So this is pretty much exactly what you have, except that I included a simple main so that I could see if it compiles, however I get:
construct.cc: In member function ‘double Fraction::GetFraction()’:
construct.cc:28: error: ‘nDenominator’ undeclared (first use of this function)
construct.cc:28: error: (each undeclared identifier is reported only once for each function it appera in).
This should have been foolprooooof! HELP!
I had a typo in the example. In GetFraction(), nDenominator should be m_nDenominator. I’ve fixed the examples in the lesson.
THANKU guys…. i was facing problem in parameterized constructor. thanks a lot again…
hello alex,
When i compiled following program i got an error that-
Ambiguity between Con::Con(int) & Con::Con(float)
my query is that Why this is so? We can do same thing with functions(i did it in this program too) then why cant with Constructors???
#include<iostream.h> #include<conio.h> class Con { float f; int i; public: Con(int it)//overloaded constructors { i=it; } Con(float ft) { f=ft; } void fun1(int p) //overloaded functions { cout<<p; } void fun2(float q) { cout<<q; } void printI() { cout<<i; } void printF() { cout<<f; } }; int main() { clrscr(); Con c(10.8); //error c.printI(); c.fun1(10); c.fun2(20.3); getch(); return 0; }The error appears because the compiler treats the number 10.8 as double by default. That is why you get the ambiguity error. Replace 10.8 with 10.8f so the compiler knows that the number is a float number.
Gotcha!!! look what i did under the comment overloaded functions!!!!
grrrr silly thing…anyways thanx man!!!!
Hi Alex,
A small suggestion on preventing the pain of date ambiguity!
Date cToday(9, 5, 2007);Could mean 9th May 2007 or 5th Sept 2007, in fact it means the former everywhere except the US.
Date cToday(2007,9, 5);Definitely means 2007 September 5th for everyone!
class Date { private: int m_nYear; int m_nMonth; int m_nDay; public: Date(int nYear=1970, int nMonth=1, int nDay=1) { m_nYear = nYear; m_nMonth = nMonth; m_nDay = nDay; } }; int main() { Date cDate; // cDate is initialized to Jan 1st, 1970 instead of garbage Date cToday(2007, 9, 5); // cToday is initialized to Sept 5th, 2007 return 0; }As long as you’re training new programers (and doing an excellent job too) I’d say this is another well recommended habit to learn in case you work on a project with an international team in the future!
Cheers,
Jamie
Hi Alex,
I have the following class:
class Point { private: int posX, posY; public: Point(int n_posX = 0, int n_posY = 0) { posX = n_posX; posY = n_posY; } // more stuff here };and using this
initializes posX to 10. Is there a way, i can initialize first constructor parameter to default value, and second one to something else?
I want to do something like this: (ofc, this example doesn’t compile)
Thanks,
Paulius
hello Alex,
This is the first time iam seeing this site actually iam
from non computer background, eventhen iam able to understand easily..
Really good job
thanks Alexji………..
Hi Alex. This has been some very helpful reading. I’m better understanding what is going on with the OOP side of C++.
At the moment I don’t have the code in front of me, but here’s the summary:
I attempted to initialize an array of values that are intended to be constant. In the object constructor, the array is initialized as a pointer, say:
class Foo {
private:
float *array;
public:
//some public variables
// more stuff here
};
Now I’m not sure what is the best way to proceed. Right now it is initialized in a function say,
Foo::foobar() {
//set array size
//set individual constant values.
// foobar’s functions
}
It works to do it this way, but I know it is a kludge to initialize an array of constant values every time the function is called.
It looks like I could do this by making a public function to do this, but how can I prevent this from being changed by functions not belonging to the class? Maybe I just need to keep reading your tutorials and I’ll find out.
But, if you find this an interesting topic, then what is the best way to initialize an array of constants that is global to the class in an object oriented language?
Ha. I figured it out. My problem was not from misusing the constructor, but that I had a loop in a cleanup function that was setting everything to 0. This was meant to clean up other arrays that DO need to be reset each time the cleanup function was called, but for some reason I put my array of constants in the cleanup, and it was doing exactly what I told it to do.
Anyone who says computers do exactly what you tell them to do…
+1 to that. :)
Using a constructor, how would you initialize a string type with a string value?
string my_str("Hello World!");I haven’t programmed for many years as OOP seemed too scary but it’s all coming together really clearly now thanks to this this site. I’m starting to see the elegance of this language. Many thanks Alex for your clear and concise tutorial.
I didn’t see any comments about #include before it is used.
I assume that it is necessary for constructors?
Tom