Go through all (Must Read *) if you don't have enough time.
C++ doesn't initialise variables automatically unlike Java. Even the class members are not initialised. So, the programmer has to define a function (user-defined function) to initialise the data members.
Let's recap the class and its members before jump into the constructors.
Program 1
class BookClub { public: void enroll(char name[]); void deposit(int id); private: char members_ [30]; int memberID_; };
This is a simple class, and _suffix used to indicate the privateness.
Class can be used in different ways.
1. Declare a variable of class type.
BookClub mem1;
2. Pass arguments of class type.
contestRun(BookClub club1);
3. Utilise class type to build other types.
class Bookstagrammer {
private:
char members_ [30];
int memberID_;
BookClub club_ [30];
};
For the below program,
Program 2
#include <iostream> using namespace std;// Ideally this should be inside .h file class BookClub { public: void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; };// This should be in .cpp fileint main() { // Object Initialisation for the class BookClub BookClub mem1; char name[30]; int id; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
Before using the members_ & memberID_, these members need to be initialised. What are the solutions?
1. Programmer defined init function
Program 3
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Memory is allocated char name[30]; int id; mem1.initBookClub(name, id); // User defined initialisation mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
What if the user forgot to initialise this BookClub?
2. Automatic initialisation method for class members using Constructor.
Constructor (Interview - Must Read *)
A constructor is a function automatically/implicitly called when a class instance is declared.
- Constructor name is a class name, and it doesn't have any explicit return type and not even void.
- It is invoked automatically when the object is created.
- Constructor should be inside public section so that class can be instantiated from anywhere.
- This is also a method of initialising the members of the class.
- Constructors can take arguments as well.
- There can be multiple constructors with the same name as class but distinguished by types of the arguments we pass. This means constructor can be overloaded.
- Constructor can't be invoked using the dot notation.
- Constructor can not be virtual, because when constructor of a class is executed there is no vtable in the memory, means no virtual pointer defined yet. Hence the constructor should always be non-virtual.
#include <iostream> // User-defined type class Foo { public: Foo(int i); // Constructor Declaration Foo() { // Constructor Definition inside class _i = 7; } int GetValue() const { return _i; } private: int _i; }; // Constructor Definition outside class Foo::Foo(int i) { _i = i; } int main() { Foo foo(5); std::cout << "Parameterised Ctor - The value of foo is " << foo.GetValue() << std::endl; Foo fooDef; std::cout << "Default Ctor - The value of foo is " << fooDef.GetValue() << std::endl; }
#include <iostream> using namespace std; class BookClub { public: BookClub(); // Constructor or zero-parameterised constructor void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; }; /* Definition of a Constructor */ BookClub::BookClub() { strcpy(members_, ""); memberID_ = 0; } int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
#include <iostream> #include <cstdio> using namespace std; class BookClub { public: BookClub(); // Constructor void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; }; /* Definition of a Constructor */ BookClub::BookClub() { strcpy(members_, ""); memberID_ = 0; } void BookClub::enroll(char name[]) { strcpy(members_, name); } void BookClub::deposit(int id) { memberID_ = id; } char* BookClub::list() { char* buffer = new char[100]; int buf_size = 100; snprintf(buffer, buf_size, "MemberName : %s MemberID : %d ", members_, memberID_); return buffer; } int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
MemberName : Shirley MemberID : 5 |
Initialising the members inside the body {} is a old school method.
As stated earlier, constructor is implicitly invoked when the object is created. We can't assign constructor to another constructor using assignment operator because it doesn't have any return value.
BookClub mem2 = BookClub("Sivy"); //This is wrong
Note: If there is no arguments for constructor, just create object instance as
BookClub mem1;
Don't do BookClub mem1();
Implementation of multiple constructors
#include <iostream> #include <cstdio> using namespace std; class BookClub { public: BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; }; /* Definition of a Default Constructor */ BookClub::BookClub() { strcpy(members_, ""); memberID_ = 0; } /* Definition of a Constructor */ BookClub::BookClub(char name[]) { strcpy(members_, name); memberID_ = 0; } /* Definition of a Constructor */ BookClub::BookClub(char name[], int id) { strcpy(members_, name); memberID_ = id; } void BookClub::enroll(char name[]) { strcpy(members_, name); } void BookClub::deposit(int id) { memberID_ = id; } char* BookClub::list() { char* buffer = new char[100]; int buf_size = 100; snprintf(buffer, buf_size, "MemberName : %s MemberID : %d ", members_, memberID_); return buffer; } int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically
BookClub mem2("Sivy");BookClub mem3("Rose", 4);char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
Here, I didn't specify the specific constructor, so the compiler had taken the default constructor which has no arguments.
What if I don't have the default constructor itself ?
In the below program, I don't have a default constructor, so the compiler complaints
Program 8
#include <iostream> #include <cstdio> using namespace std; class BookClub { public: //BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; }; /* Definition of a Default Constructor */ /*BookClub::BookClub() { strcpy(members_, ""); memberID_ = 0; }*/ /* Definition of a Constructor */ BookClub::BookClub(char name[]) { strcpy(members_, name); memberID_ = 0; } /* Definition of a Constructor */ BookClub::BookClub(char name[], int id) { strcpy(members_, name); memberID_ = id; } void BookClub::enroll(char name[]) { strcpy(members_, name); } void BookClub::deposit(int id) { memberID_ = id; } char* BookClub::list() { char* buffer = new char[100]; int buf_size = 100; snprintf(buffer, buf_size, "MemberName : %s MemberID : %d ", members_, memberID_); return buffer; } int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
In function 'int main()':
Line 57: error: no matching function for call to 'BookClub::BookClub()'
compilation terminated due to -Wfatal-errors. |
This would be more common problem in inheritance.
Constructors' can't be inherited.
Consider primitive data type as constructor.
int num; // Default constructor is called
int num(0); // It accepts one argument and initialises the integer to the passed value
int num = 0; // This is called copy constructor
There is another problem if the constructors are not called cautiously or sequentially.
Program 9
class Bookstagrammer { BookClub mem3; // mem3 object creation BookClub mem4; Boostagrammer() { mem3("Dolly"); // Before reaching here mem4("Agnes", 45); } }; class BookClub { public: BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; };
In the above example, when the Bookstagrammer object is created, it will call the BookClub constructor as there are mem3 and mem4 variables in Bookstagrammer class. So, before calling the Bookstagrammer constructor "Before reaching here" the variables mem3 and mem4 is initialised with the help of BookClub Constructor. This is a kind of duplication.
If we don't have the BookClub default constructor, we will again be into trouble.
Parameterised Constructor (Member Initialisation List) (Must Read *)
#include <iostream> // User-defined type class Foo { public: Foo(int i); // Constructor Declaration Foo() : _i(7) {} // Constructor Definition // ": _i(5)" is initialiser list // ": _i(5) {}" is function body int GetValue() const { return _i; } private: int _i; }; /* Constructor Definition * where ": _i(i)" is initialiser list */ Foo::Foo(int i) : _i(i) { /* std::cout << " Foo - Parametersised Ctor" << std::endl; */ } int main() { Foo foo(5); std::cout << "Parameterised Ctor - The value of foo is " << foo.GetValue() << std::endl; Foo fooDef; std::cout << "Default Ctor - The value of foo is " << fooDef.GetValue() << std::endl; return 0; }
This is to avoid invoking the constructor twice.
Program 11
class Bookstagrammer { BookClub mem3; // mem3 object creation BookClub mem4; Boostagrammer(): mem3("Dolly"), mem4("Agnes") { // Before reaching here } }; class BookClub { public: BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; };
In the comment line 'Before reaching here', the member variables are initialised using the parameterised constructor.
Program 12
class Bookstagrammer { BookClub mem3; // mem3 object creation BookClub mem4; Boostagrammer(): memberID_(0.0) { // Before reaching here } }; class BookClub { public: BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); private: char members_ [30]; int memberID_; };
It is good to use this parameterised constructor, otherwise there will be a situation, you can't initialise the parent class properly.
In the below example, pointer is created for the class, but the memory is not allocated.
Program 13 (Must Read *)
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically BookClub mem2("Sivy"); BookClub mem3("Rose", 4); BookClub *mem6; // Constructor will not be called char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
Memory will be allocated only when the new is used, and the constructor is also called.
Program 14 (Must Read *)
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically BookClub mem2("Sivy"); BookClub mem3("Rose", 4); BookClub *mem6; // Constructor will not be called mem6 = new BookClub; // Constructor will be called BookClub *mem7 = new BookClub[10]; // 10 objects will be created BookClub *mem8 = new BookClub("Alehka", 10); char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
When you want to create 10 objects, we can do in the following way.
BookClub *mem7 = new BookClub[10]; // 10 objects will be created
The following would happen if we 'new' for (non-pointer) objects.
Program 15
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically BookClub mem9 = new BookClub; char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
error: conversion from 'BookClub*' to non-scalar type 'BookClub' requested
Click here to learn about constructor initialiser list.
DestructorsDestructor is called automatically when the class instance is destroyed. It is used to clean-up the memory.
It allows the programmer to write the necessary code to free the resources acquired by the object prior to deleting the object itself.
Syntax
myclass *myobject = new myclass(); delete(myobject);
The above is the appropriate way of removing the object.
Program 16
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically BookClub *mem6; // Constructor will not be called mem6 = new BookClub; // Constructor will be called /* Do something */ delete mem6; >>>>>>>>>>>>>>>>>>>> char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); return 0; }
Here, mem6's memory will be removed by delete as well as destructor.
The below is destructor syntax, but calling destructor is not needed.
Method 1:
class BookClub { public: BookClub(); // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); ~BookClub(); >>>>>>>>>>>>>>>>>>>> private: char members_ [30]; int memberID_; };
Method 2: (Must Read *)
int main() { // Object Initialisation for the class BookClub BookClub mem1; // Constructor is called automatically char name[30] = "Shirley"; int id = 5; mem1.enroll(name); mem1.deposit(id); cout << mem1.list(); mem1.~BookClub(); >>>>>>>>>>>>>> return 0; }
Calling this will make double free as the memory will be freed when the object instance is deleted.
If we need to initialise the variables of base, first call the base constructor and call the derived class constructor.
It is good practice to have the destructor as virtual, otherwise we might call the sub class's destructor. So, the right version of destructor will be called if the base is declared with virtual. Click here to read about virtual destructor.
const
It is an accessor and will not change the state of the object.
int getName() const { return name; }
const int mx = numeric_limits<int>::max();
Destructors are called when one of the following event occurs:
- A local (automatic) object with block scope goes out of scope.
- An object allocated using the new operator is explicitly deallocated using delete().
- The lifetime of a temporary object ends.
= delete
- Prevents defaults from being used.
- If you forget to delete the default constructor, the delete keyword ensures that compiler doesn't provide by default.
class BookClub { public: BookClub() = delete; // Default Constructor BookClub(char name[]); BookClub(char name[], int id); void enroll(char name[]); void deposit(int id); char* list(); ~BookClub(); private: char members_ [30]; int memberID_; };
Final
In C++11, similar to Java, a method that is declared final
in the super class cannot be overridden; also, a method can be declared override
to make the compiler check that it overrides a method in the base class.
A method which is declared final in the superclass can't be overridden. If happens, it will throw the below error.
In file included from Army.cpp:1:
Army.h:86:10: error: virtual function ‘virtual void ArmyHospital::transfer(int, string)’ overriding final function
86 | void transfer(int x, string name) override;
Army.h:41:15: note: overridden function is ‘virtual void Army::transfer(int, string)’
41 | virtual void transfer(int x, string y) final;
| ^~~~~~~~~
If the function is declared as virtual final, no need to redeclare them in the derived class.
Why constructor is not virtual? (Must Read*)
Virtual functions basically provide polymorphic behavior. That is, when you work with an object whose dynamic type is different than the static (compile time) type with which it is referred to, it provides behavior that is appropriate for the actual type of object instead of the static type of the object.
Now try to apply that sort of behavior to a constructor. When you construct an object the static type is always the same as the actual object type since:
To construct an object, a constructor needs the exact type of the object it is to create [...] Furthermore [...]you cannot have a pointer to a constructor
Comments
Post a Comment