Since C++ is a superset of C, we can use functions in C++ in the same way that we use them in C. While this is great and all, let’s focus on functions within the OOP paradigm.
The simplest way to attach a function to an object is to include the function in the description of the class. Functions can be placed in either the public, protected, or private sections of a class.
#include <iostream> #include <cmath> using namespace std; class Square{ private: double _dSide; protected: double diagonal(){ return _dSide * sqrt(2); } public: Square(double paramSide = 0.0) { _dSide = paramSide;} double area(){ return _dSide * _dSide; } double perimeter() { return _dSide * 4; } }; int main(void){ //instantiate object of class //square Square objSq(7); cout << "square has a perimeter of : " << objSq.perimeter() << endl; cout << "square has an area of : " << objSq.area() << endl; return 0; }
Functions that are part of a class are called member functions. The most ubiquitous member function is the constructor, which creates and initializes an instance of an object of a class. This initialization can use default values or assign no values whatsoever.
A constructor function for a class must have the same name as the name of the class. A constructor function cannot have any return value, not even void. If no constructor is specified, then the compiler will provide a public default constructor. Of particular gravitas is the fact that constructors cannot be inherited from a base class, although we can call a base constructor from the derived class.
The most common mechanism for removing objects from memory is the destructor member function. The notation for a desctructor function is to precede the name of the class with a tilde symbol. Like a constructor function, a destructor cannot have a return value. Destructor functions, unlike constructors, cannot take a
#include <iostream> using namespace std; class CDynamicIntArray{ private: int *_iArrPtr; int *_iPtrEnd; int *_iPtrStart; public: CDynamicIntArray(int n = 10); ~CDynamicIntArray(); int increment(); int decrement(); void reset(); int getValue(); void assignValue(int n = 0); }; //constructor //create dynamic int array with new //define bounds of array with poiter arithmetic CDynamicIntArray::CDynamicIntArray(int n){ _iArrPtr = new int[n]; _iPtrEnd = _iArrPtr + n; _iPtrStart = _iArrPtr; } //destructor //free up pointers CDynamicIntArray::~CDynamicIntArray(){ _iArrPtr = _iPtrStart; delete [] _iArrPtr; } int CDynamicIntArray::increment(){ if((_iArrPtr + 1) < _iPtrEnd){ return *(++_iArrPtr); } else { return 0; } } int CDynamicIntArray::decrement(){ //check if still in bounds //and return value if(((_iArrPtr - 1) < _iPtrStart)==false){ return *(--_iArrPtr); } else { return 0; } } void CDynamicIntArray::reset(){ _iArrPtr = _iPtrStart; } int CDynamicIntArray::getValue(){ return *_iArrPtr; } void CDynamicIntArray::assignValue(int n){ *_iArrPtr = n; if((_iArrPtr+1) < _iPtrEnd){ _iArrPtr++; } } int main(void){ CDynamicIntArray objDynIArr; objDynIArr.assignValue(1138); objDynIArr.assignValue(128); objDynIArr.assignValue(192); objDynIArr.assignValue(224); objDynIArr.assignValue(240); objDynIArr.assignValue(248); objDynIArr.assignValue(80386); objDynIArr.assignValue(451); objDynIArr.assignValue(1999); objDynIArr.assignValue(47); //won't assign value objDynIArr.assignValue(2001); objDynIArr.reset(); cout << objDynIArr.getValue() << endl; cout << objDynIArr.increment() << endl; //last two values will be 0 //as we have passed the bounds of the array for(int i = 0; i < 10; i++){ cout << objDynIArr.increment() << endl; } return 0; }
Constructors and destructors behave differently from other member functions, especially in the context of inheritance. When a derived-class variable is created, the constructor for the base class is called first, followed by the constructor for the derived class.
#include <iostream> using namespace std; class CBaseClass{ public: CBaseClass(){ cout << "CBaseClass constructor called." << endl; } ~CBaseClass(){ cout << "CBaseClass destructor called." << endl; } }; class CDerivedClass : CBaseClass { public: CDerivedClass(){ cout << "CDerivedClass constructor called." << endl; } ~CDerivedClass(){ cout << "CDerivedClass destructor called." << endl; } }; int main(void){ CDerivedClass objDerived; return 0; }
After the object variable is destroyed, the class destructors are called. The destructor for the derived class is called first, then the destructor for the base class. Nota bene! Derived classes can operate as base classes. Because of this, we should make the destructor for a base class virtual. Normally, the keyword virtual means “call the derived class function instead of the equivalent base class function”, but destructors are a special case. When a virtual destructor is encountered, the destructor of the derived class will be called, and then the desctructor of the base class will be called.