Upload
others
View
14
Download
0
Embed Size (px)
Citation preview
C++ Programming Languages
Lecturer: OmidOmid JafarinezhadJafarinezhad
Fall 2013
Lecture 4: Object Oriented Programming
Department of Computer EngineeringDepartment of Computer Engineering 1
Outline
•• objectobject--oriented programmingoriented programming –– Classes and class membersClasses and class members
–– Access functions and encapsulationAccess functions and encapsulation
–– Constructors and Destructors Constructors and Destructors
–– Class code and header filesClass code and header files
–– Operator overloadingOperator overloading
–– CompositionComposition
–– InheritanceInheritance
–– Virtual FunctionsVirtual Functions
–– Template classesTemplate classes
–– ExceptionsExceptions
2
Classes and class members
3
structstruct DateStruct { int nMonth; int nDay; int nYear; }; // Here is a function to initialize a date// Here is a function to initialize a date void SetDate(DateStruct &sDate, int nMonth, int nDay, int nYear) { sDate.nMonthsDate.nMonth = nMonth; sDate.nDay = nDay; sDate.nYear = nYear; } // … In main …// … In main … DateStructDateStruct sToday; // Initialize it manually// Initialize it manually sToday.nMonth = 10; sToday.nDay = 14; sToday.nYear = 2040; SetDateSetDate((sTodaysToday, , 1010, , 1414, , 20202020););
Classes and class members
4
struct DateStruct { int nMonth; int nDay; int nYear; };
classclass DateDate
{{ publicpublic:: intint m_nMonthm_nMonth;; intint m_nDaym_nDay;; intint m_nYearm_nYear;; };}; Date cToday; // declare a // declare a ObjectObject of of classclass DateDate // Assign values to our members using the member selector operator (.)// Assign values to our members using the member selector operator (.) cToday.m_nMonth = 10; cToday.m_nDay = 14; cToday.m_nYear = 2020;
Classes and class members
5
structstruct DateStruct {{ int nMonth; int nDay; int nYear; };}; // Here is a function to initialize a date// Here is a function to initialize a date void SetDate(DateStruct &sDate, int nMonth, int nDay, int nYear) { sDate.nMonthsDate.nMonth = nMonth; sDate.nDay = nDay; sDate.nYear = nYear; } DateStructDateStruct sToday; SetDateSetDate((sTodaysToday, , 1010, , 1414, , 20202020););
classclass Date {{ public:public: int m_nMonth; int m_nDay; int m_nYear; // Member function// Member function void void SetDateSetDate((intint nMonthnMonth, , intint nDaynDay, , intint nYearnYear)) {{ m_nMonthm_nMonth = = nMonthnMonth;; m_nDaym_nDay = = nDaynDay;; m_nYearm_nYear = = nYearnYear;; }} };}; DateDate cToday; // call // call SetDateSetDate() on () on cTodaycToday cToday.SetDate(10, 14, 2020);
Classes and class members
6
#include #include <iostream> classclass EmployeeEmployee { publicpublic: char m_strName[25]; int m_nID; double m_dWage; // // Set the employee informationSet the employee information void SetInfo(char *strName, int nID, double dWage) { strncpy(m_strName, strName, 25); m_nID = nID; m_dWage = dWage; } // // Print employee information to the screenPrint employee information to the screen void Print() { using namespace std; cout << "Name: " << m_strName << " Id: " << m_nID << " Wage: $" << m_dWage << endl; } };
EmployeeEmployee cAlex; cAlex.SetInfocAlex.SetInfo("Alex", ("Alex", 11, , 2525..0000);); EmployeeEmployee cJoe; cJoe.SetInfo("Joe", 2, 22.25); cAlex.PrintcAlex.Print();(); cJoe.Print();
Public vs private access specifiers
7
classclass Access { int m_nA; // private by default// private by default int GetA() { return m_nA; } // private by default// private by default private:private: int m_nB; // private// private int GetB() { return m_nB; } // private// private protected:protected: int m_nC; // protected// protected int GetC() { return m_nC; } // protected// protected public:public: int m_nD; // public// public int GetD() { return m_nD; } // public// public };
AccessAccess cAccess; // ok because // ok because m_nDm_nD is publicis public cAccess.m_nD = 5; // ok because // ok because GetDGetD() is public() is public std::cout << cAccess.GetD(); // WRONG because // WRONG because m_nAm_nA is privateis private cAccess.m_nA = 2; // WRONG because // WRONG because GetBGetB() is private() is private std::cout << cAccess.GetB();
Access functions and encapsulation
8
classclass Date {
private:private: intint m_nMonth; intint m_nDay; intint m_nYear;
public:public: // Getters// Getters int GetGetMonth() { returnreturn m_nMonth; } int GetDay() { returnreturn m_nDay; } int GetYear() { returnreturn m_nYear; } // Setters// Setters voidvoid SetSetMonth(intint nMonth) { m_nMonth = nMonth; } voidvoid SetDay(int nDay) { m_nDay = nDay; } voidvoid SetYear(int nYear) { m_nYear = nYear; } };
Public vs private access specifiers
9
classclass Change {
public:public: intint m_nValuem_nValue;; }; int main() { Change cChange; cChange.m_nValuecChange.m_nValue = = 55;; std::cout << cChange.m_nValuecChange.m_nValue << std::endl; };
classclass Change { private:private: intint m_nValuem_nValue;; public:public: void void SetValueSetValue((intint nValuenValue) { ) { m_nValuem_nValue = = nValuenValue; }; } intint GetValueGetValue() { return () { return m_nValuem_nValue; }; } }; int main() { Change cChange; cChange.SetValuecChange.SetValue((55);); std::cout << cChange.GetValuecChange.GetValue() () << std::endl; }
Constructors
• A constructorconstructor is a special kind of classclass membermember functionfunction that is executedexecuted whenwhen anan objectobject ofof thatthat classclass isis instantiatedinstantiated
• Constructors are typically used to initializeinitialize membermember variablesvariables of the class to appropriate default values, oror toto allowallow thethe useruser toto easilyeasily initializeinitialize thosethose membermember variablesvariables to whatever values are desired
• Constructors have specific rules for how they must be named: – Constructors should always have the same name as the class always have the same name as the class – Constructors havehave nono returnreturn typetype (not(not eveneven void)void)
• A constructorconstructor thatthat takestakes nono parametersparameters (or has all optional
parameters) is called a defaultdefault constructorconstructor
10
Constructors
11
class FractionFraction { private: int m_nNumerator; int m_nDenominator; public:
Fraction()Fraction() // default constructor// default constructor
{ m_nNumerator = 0; m_nDenominator = 1; } int GetNumerator() { return m_nNumerator; } int GetDenominator() { return m_nDenominator; } // …// … };
Fraction Fraction cDefaultcDefault; ; // calls Fraction() constructor// calls Fraction() constructor
Constructors with parameters
12
class FractionFraction { private: int m_nNumerator; int m_nDenominator; public:
Fraction()Fraction() { // default constructor// default constructor m_nNumerator = 0; m_nDenominator = 1; } // // Constructor with parametersConstructor with parameters
FractionFraction((intint nNumeratornNumerator, , intint nDenominatornDenominator = = 11)) { assert(nDenominator != 0); m_nNumerator = nNumerator; m_nDenominator = nDenominator; } int GetNumerator() { return m_nNumerator; } int GetDenominator() { return m_nDenominator; } // …// … };
Fraction Fraction cDefaultcDefault; ; // calls Fraction() constructor// calls Fraction() constructor Fraction Fraction cFiveThirdscFiveThirds((55, , 33); ); // calls Fraction(// calls Fraction(intint, , intint) constructor) constructor Fraction Six(Fraction Six(66); ); // calls Fraction(// calls Fraction(intint, , intint) constructor) constructor
Private constructors
13
class Book { private: int m_nPages; // This constructor can only be used by Book's members// This constructor can only be used by Book's members Book()Book() // private default constructor// private default constructor { m_nPages = 0; } public: // This constructor can be used by anybody// This constructor can be used by anybody Book(Book(intint nPagesnPages) ) // public non// public non--default constructordefault constructor { m_nPages = nPages; } }; Book Book cMyBookcMyBook; ; // fails because default constructor Book() is private// fails because default constructor Book() is private
Book Book cMyOtherBookcMyOtherBook((242242); ); // ok because Book(// ok because Book(intint) is public) is public
Constructor chaining issues
14
class Foo { public: FooFoo() () { // code to do A// code to do A } FooFoo((intint nValuenValue)) { // code to do A// code to do A // code to do B// code to do B } };
class Foo { public: FooFoo()() { Init();Init(); } FooFoo((intint nValuenValue)) { Init();Init(); // code to do B// code to do B } void Init()void Init() { // code to do A// code to do A } };
// C++// C++1111 : : // delegating constructors// delegating constructors class Foo { public: FooFoo()() { // code to do A// code to do A } FooFoo((intint nValuenValue) : ) : FooFoo()() { // code to do B// code to do B } };
Destructors • destructor is called when an object is destroyedwhen an object is destroyed
• Like constructors, destructors have specific
naming rules: – The destructor must have the same name as the the same name as the
class, preceded by a tilde (~)class, preceded by a tilde (~)
– The destructor can not take argumentscan not take arguments • implies that only one destructor may exist per classone destructor may exist per class, as
there is no way to overload destructorsno way to overload destructors since they can not be differentiated from each other based on arguments
– The destructor has no return typehas no return type
15
Destructors
16
classclass MyString { privateprivate: char *m_pchString; int m_nLength; public:public: MyStringMyString(const char *(const char *pchStringpchString="") ="") { m_nLength = strlen(pchString) + 1; //Plus one character for a terminator//Plus one character for a terminator m_pchString = new char[m_nLength]; strncpy(m_pchString, pchString, m_nLength); m_pchString[m_nLength-1] = '\0'; // Make sure the string is terminated// Make sure the string is terminated }
~~MyStringMyString() { () { // destructor// destructor delete[] delete[] m_pchStringm_pchString; ; // We need to // We need to deallocatedeallocate our bufferour buffer m_pchStringm_pchString = = 00; ; // Set // Set m_pchStringm_pchString to null just in caseto null just in case }} char* GetString() { return m_pchString; } int GetLength() { return m_nLength; } };
int main() { MyStringMyString cMyName("Alex"); // call constructor// call constructor std::cout << "My name is: " << cMyName.GetString(); return 0; } // // cMyNamecMyName destructor called here!destructor called here!
The hidden “this” pointer
17
class Simple { private: intint m_nIDm_nID;; public: Simple(int nID) { SetID(nID); } void SetID(int nID) {thisthis--> > m_nID = nID; } int GetID() { return thisthis-->>m_nID; } };
void SetID(int nID) { m_nID = nID; } // becomes (by compiler):// becomes (by compiler): void SetID(Simple* const thisSimple* const this, int nID) { thisthis-->>m_nID = nID; }
The hidden “this” pointer
18
class Calc { private: int m_nValue; public: Calc() { m_nValue = 0; } void Add(int nValue) { m_nValue += nValue; } void Sub(int nValue) { m_nValue -= nValue; } void Mult(int nValue) { m_nValue *= nValue; } int GetValue() { return m_nValue; } };
Calc cCalc; cCalc.Add(5); cCalc.Sub(3); cCalc.Mult(4);
The hidden “this” pointer
19
class Calc { private: int m_nValue; public: Calc() { m_nValue = 0; } Calc&Calc& Add(int nValue) { m_nValue += nValue; return *this; return *this; } Calc& Sub(int nValue) { m_nValue -= nValue; return *this; } Calc& Mult(int nValue) { m_nValue *= nValue; return *this; } int GetValue() { return m_nValue; } };
Calc cCalc; cCalc.AddcCalc.Add((55).Sub().Sub(33).).MultMult((44););
Class code and header files
20
class Date { private: int m_nMonth; int m_nDay; int m_nYear; Date() { } Date() { } // private default constructor// private default constructor public: DateDate(int nMonth, int nDay, int nYear) { SetDate(nMonth, nDay, nYear); } void SetDate(int nMonth, int nDay, int nYear) { m_nMonth = nMonth; m_nDay = nDay; m_nYear = nYear; } int GetMonth() { return m_nMonth; } int GetDay() { return m_nDay; } int GetYear() { return m_nYear; } };
// // Date.hDate.h ##ifndefifndef DATE_HDATE_H #define DATE_H#define DATE_H // class interface// class interface // class declaration// class declaration ##endifendif
// Date.cpp// Date.cpp // class implementation// class implementation
Class code and header files
21
////Date.hDate.h ##ifndefifndef DATE_HDATE_H #define DATE_H#define DATE_H class Date class Date { privateprivate: int m_nMonth; int m_nDay; int m_nYear; Date() ;Date() ; publicpublic: Date(Date(intint nMonthnMonth, , intint nDaynDay, , intint nYearnYear);); void void SetDateSetDate((intint nMonthnMonth, , intint nDaynDay, , intint nYearnYear);); intint GetMonthGetMonth() ;() ; intint GetDayGetDay()() ;; intint GetYearGetYear() ;() ; }; ##endifendif
Class code and header files
22
//Date.cpp//Date.cpp #include "#include "Date.hDate.h"" // Date constructor// Date constructor Date::Date(){}Date::Date(){} Date::Date::Date(int nMonth, int nDay, int nYear) { SetDate(nMonth, nDay, nYear); } // Date member function// Date member function void Date::Date::SetDate(int nMonth, int nDay, int nYear) { m_nMonth = nMonth; m_nDay = nDay; m_nYear = nYear; } int Date:: Date:: GetMonth() { return m_nMonth; } int Date:: Date:: GetDay() { return m_nDay; } int Date:: Date:: GetYear() { return m_nYear; } };
Const class objects and member functions
• If a class is not initialized using aa parameterizedparameterized constructor,constructor, aa
publicpublic defaultdefault constructorconstructor mustmust bebe providedprovided — if no public
default constructor is provided in this case, a compiler error will occur
• Once a constconst classclass objectobject hashas beenbeen initializedinitialized viavia constructorconstructor, any attemptattempt toto modifymodify the member variables of the object is disalloweddisallowed
23
constconst intint nValuenValue = = 55; ; // initialize explicitly// initialize explicitly constconst intint nValuenValue22((77); ); // initialize // initialize implictlyimplictly constconst Date Date cDatecDate; ; // initialize using default constructor// initialize using default constructor constconst Date cDateDate cDate22((1010, , 1616, , 20202020); ); // initialize using parameterized constructor// initialize using parameterized constructor
Const class objects and member functions
24
class Something { public: int m_nValue; Something() { m_nValue = 0; } void ResetValue() { m_nValue = 0; } void SetValue(int nValue) { m_nValue = nValue; } int GetValue() { return m_nValue; } }; int main() { constconst Something cSomething; // calls default constructor// calls default constructor cSomething.m_nValuem_nValue = 5; // Error: violates const// Error: violates const cSomething.ResetValueResetValue(); // Error: violates const// Error: violates const cSomething.SetValueSetValue(5); // Error: violates const// Error: violates const std::cout << cSomething.GetValueGetValue(); // Error !!!// Error !!! }
Const class objects and member functions
•• constconst classclass objectsobjects cancan onlyonly callcall constconst membermember functionsfunctions, GetValue() has not been marked as a const member function. A constconst membermember functionfunction is a member function that guarantees itit willwill notnot changechange anyany classclass variablesvariables oror callcall anyany nonnon--constconst membermember functionsfunctions
25
class Something { int m_nValue; public: /*…*//*…*/
int GetValue() constconst { return m_nValue; }
}; int main() { constconst Something cSomething; std::cout << cSomething.GetValueGetValue(); // Ok// Ok }
Const class objects and member functions
26
class Something { int m_nValue; public: /*…*//*…*/
int GetValue() constconst;
};
int Something ::GetValue() constconst { return m_nValue; } int main() { constconst Something cSomething; std::cout << cSomething.GetValueGetValue(); // Ok// Ok }
Const class objects and member functions
•• NoteNote thatthat constructorsconstructors shouldshould notnot bebe markedmarked asas constconst. This is because const objects should initialize their member variables, and a const constructor would not be able to do so
• ResetValue() has been marked as a const member function, but it attempts to change m_nValue. ThisThis willwill causecause aa compilercompiler errorerror
27
class Something { int m_nValue; public: /*…*//*…*/
void ResetValue () constconst {m_nValue = 0 ; }
};
Const class objects and member functions
• although it is not done very often, itit isis possiblepossible toto overloadoverload aa functionfunction inin suchsuch aa wayway toto havehave aa constconst andand nonnon--constconst versionversion ofof thethe samesame functionfunction
28
class Something { int m_nValue; public: Something(){} const const intint& & GetValue() constconst { return m_nValue; } intint&& GetValue() { return m_nValue; } }; SomethingSomething cSomething; cSomething.GetValue(); // calls non// calls non--const const GetValueGetValue();(); const Something const Something cSomething2; cSomething2.GetValue(); // calls const // calls const GetValueGetValue();();
Static member variables
•• file scope and the static keywordfile scope and the static keyword
29
int GenerateID() { static static intint s_nIDs_nID = = 00;; return s_nID++; } int main() { std::cout << GenerateIDGenerateID() () << std::endl; // // 00 std::cout << GenerateIDGenerateID()() << std::endl; // // 11 std::cout << GenerateIDGenerateID() () << std::endl; // // 22 return 0; }
Static member variables
30
class Something { private: int m_nValue; public: Something(int value) { m_nValue = value; } int GetValue() {return m_nValue;} }; int main() { Something cFirst(1); Something cSecond(2); coutcout <<<<cFirst.GetValuecFirst.GetValue()() << << cSecond.GetValuecSecond.GetValue(); // (); // 1212 return 0; }
class Something { public: static static intint s_nValues_nValue;; }; intint Something::Something::s_nValues_nValue = = 11;; int main() { Something cFirst; cFirst.s_nValuecFirst.s_nValue = = 22;; Something cSecond; cout << cSecond.s_nValuecSecond.s_nValue << cFirst.s_nValuecFirst.s_nValue; // // 2222 return 0; }
Static member variables • Although you can access static members through objects of
the class type: – this is somewhat misleading. cFirstcFirst..s_nValues_nValue impliesimplies thatthat s_nValues_nValue
belongsbelongs toto cFirstcFirst,, andand thisthis isis reallyreally notnot thethe casecase
– s_nValue does not belong to any object. In fact, In fact, s_nValues_nValue exists even exists even if there are no objects of the class have been instantiated!if there are no objects of the class have been instantiated!
• it is better to think of staticstatic membersmembers asas belongingbelonging toto thethe classclass itself,itself, notnot thethe objectsobjects ofof thethe classclass. Because s_nValue exists independently of any class objects, it can be accessed directly using the class name and the scope operator:
31
class SomethingSomething { public: static static intint s_nValues_nValue;; };
int main() { SomethingSomething::::s_nValues_nValue = = 22;; std::cout << Something::Something::s_nValues_nValue; }
An example of Static member variables
32
class Something { private: staticstatic int s_nIDGenerator; int m_nID; public: Something() { Something() { m_nIDm_nID = = s_nIDGenerators_nIDGenerator++; }++; } int GetID() constconst { return m_nID; } }; int Something::s_nIDGenerator = 1; int main() { Something cFirst; Something cSecond; Something cThird; cout << cFirst.GetIDcFirst.GetID() () << endl; cout << cSecond.GetIDcSecond.GetID() () << endl; cout << cThird.GetIDcThird.GetID() () << endl; return 0; }
Class in UML Class Diagrams
• A class in an object oriented system provides a crispcrisp abstractionabstraction of a well defined set of responsibilitiesresponsibilities
•• visvis = visibility= visibility –– + + denotes public attributes or operations
–– -- denotes private attributes or operations
–– ## denotes protected attributes or operations
•• attributeattribute = data member (aka fieldfield)
•• operationoperation = methodmethod (or constructorconstructor)
•• static members static members are indicated by underliningunderlining
33
ClassNameClassName
visvis attributeattribute :: typetype
visvis operationoperation((argarg listlist) :) : return typereturn type
Class in UML Class Diagrams
34
Exercises •• writing a singleton classwriting a singleton class
•• DesignDesign aa classclass diagramdiagram forfor modelingmodeling relationshiprelationship betweenbetween studentstudent andand theirtheir currentcurrent semestersemester coursescourses
–– Noun/verbNoun/verb analysisanalysis (Grammatical(Grammatical Parsing)Parsing)
•• LookLook forfor nounsnouns oror nounnoun phrasesphrases -- thesethese areare candidatecandidate classesclasses oror attributesattributes
•• LookLook forfor verbsverbs oror verbverb phrasesphrases -- thesethese areare candidatecandidate responsibilitiesresponsibilities oror operationsoperations ((InformationInformation expertexpert))
35
Friend functions
36
class Accumulator { private: int m_nValue; public: Accumulator() { m_nValue = 0; } void Add(int nValue) { m_nValue += nValue; } // Make the Reset() function a friend of this class// Make the Reset() function a friend of this class
friendfriend void Reset(Accumulator &cAccumulator);
}; // Reset() does not have a *this pointer// Reset() does not have a *this pointer // Reset() is now a friend of the Accumulator class// Reset() is now a friend of the Accumulator class void Reset(Accumulator &Accumulator &cAccumulatorcAccumulator) { // can access the private data of Accumulator objects// can access the private data of Accumulator objects cAccumulator.m_nValuecAccumulator.m_nValue = = 00;; }
Friend functions
37
// A function can be a friend of more than one class// A function can be a friend of more than one class class Humidity;class Humidity; class Temperature { int m_nTemp; public: Temperature(int nTemp) { m_nTemp = nTemp; } friendfriend void PrintWeatherPrintWeather(Temperature &cTemperature, Humidity &cHumidity); }; class Humidity { int m_nHumidity; public: Humidity(int nHumidity) { m_nHumidity = nHumidity; } friend friend void PrintWeatherPrintWeather(Temperature &cTemperature, Humidity &cHumidity); }; void PrintWeatherPrintWeather(Temperature &cTemperature, Humidity &cHumidity) { std::cout << cTemperature.m_nTempcTemperature.m_nTemp << cHumidity.m_nHumiditycHumidity.m_nHumidity << std::endl; }
Friend classes
38
class class Display;Display; class Storage { int m_nValue; double m_dValue; public: Storage(int nValue, double dValue) { m_nValue = nValue; m_dValue = dValue; } // Make the Display class a friend of Storage// Make the Display class a friend of Storage
friendfriend classclass Display;
};
class Display class Display { /* … *//* … */
public: Display() { } void DisplayItem(Storage &cStorage) { cout << cStorage.m_nValuecStorage.m_nValue << " " << cStorage.m_dValuecStorage.m_dValue; } };
Storage cStorage(5, 6.7); Display cDisplay(); cDisplay.DisplayItem(cStorage);
class Display; class Storage { int m_nValue; double m_dValue; public: Storage(int nValue, double dValue) { m_nValue = nValue; m_dValue = dValue; } friend class Display; }; class Display { /* … */ public: Display() { } void DisplayItem(Storage &Storage &cStoragecStorage) { cout << cStorage.m_nValue << " " << cStorage.m_dValue; } };
Friend classes
39
Storage Storage
-- m_dValuem_dValue::: : doubledouble -- m_nValuem_nValue :: intint
+ Storage(+ Storage(intint nValuenValue, double , double dValuedValue))
Display Display
+ + Display() Display() + + DisplayItemDisplayItem(Storage &(Storage &cStoragecStorage) : void) : void
DependencyDependency is aa weakerweaker formform ofof relationshiprelationship which indicates that one class depends on another because it uses it at some point in time. OneOne classclass dependsdepends onon anotheranother ifif thethe independentindependent classclass isis aa parameterparameter variablevariable oror locallocal variablevariable ofof aa methodmethod ofof thethe dependentdependent classclass
Anonymous variables and objects
40
int Add(int nX, int nY) { int nSum = nX + nY; return return nSumnSum;; } // An// An anonymous variableanonymous variable is a variable that is given no nameis a variable that is given no name int AnonymousAdd(int nX, int nY) { return return nXnX + + nYnY;; } int main() { cout << Add(Add(55, , 33);); cout << AnonymousAddAnonymousAdd ((55, , 33);); }
Anonymous variables and objects
41
class Cents{ int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } int GetCents() { return m_nCents; } }; Cents Add(Cents &c1, Cents &c2) { Cents cTemp(c1.GetCents() + c2.GetCents()); return return cTempcTemp;; } int main() { Cents cCents1(6); Cents cCents2(8); Cents cCentsSum = Add(cCentsAdd(cCents11, cCents, cCents22)); cout << "I have " << cCentsSum.GetCentscCentsSum.GetCents() () << " cents."; }
Anonymous variables and objects
42
class Cents { int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } int GetCents() { return m_nCents; } }; Cents Add(Cents &c1, Cents &c2) { return CentsCents(c1.GetCents() + c2.GetCents()); } int main() { Cents cCents1(6); Cents cCents2(8); cout << "I have " << Add(cCentsAdd(cCents11, cCents, cCents22).).GetCentsGetCents() () << " cents."; }
Cents cCents(5); // normal variable// normal variable // // initialize it and then destroy itinitialize it and then destroy it Cents(Cents(77); ); // // anonymousanonymous variablevariable Cents(Cents(1010).).GetCentsGetCents()(); ; // // 1010
Overloading the arithmetic operators •• OperatorOperator overloadingoverloading allows the programmer to
define how operators (suchsuch asas +,+, --,, ==,==, =,=, andand !!) should interact with various data types
•• Operators as functionsOperators as functions – When you see the expression nXnX ++ nYnY, you can
translate this in your head to operator+(operator+(nXnX,, nYnY)) –– function overloading function overloading is used to resolve the function
calls to different versions of the function
• you can onlyonly overloadoverload thethe operatorsoperators thatthat existexist
– You cancan notnot createcreate newnew operatorsoperators • e.g., you could not create an operator ** to do exponents
43
Overloading the arithmetic operators
44
class Cents { int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } // Add Cents + Cents// Add Cents + Cents friendfriend Cents operator+operator+(const Cents &c1, const Cents &c2); int GetCents() { return m_nCents; } }; // note: this function is not a member function!// note: this function is not a member function! Cents operator+operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(// use the Cents constructor and operator+(intint, , intint)) return Cents(c1.m_nCents + c2.m_nCents); } int main() { Cents cCents1(6); Cents cCents2(8); Cents cCentsSum = cCentscCents1 1 + cCents+ cCents22; std::cout << "I have " << cCentsSumcCentsSum ..GetCentsGetCents() () << " cents." ; }
(cCentscCents1 1 + cCents+ cCents22) .GetCentsGetCents(); ();
Overloading the arithmetic operators
45
class Cents { int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } // Add Cents + Cents// Add Cents + Cents friendfriend Cents operator+operator+(const Cents &c1, const Cents &c2); int GetCents() { return m_nCents; } }; // note: this function is not a member function!// note: this function is not a member function! Cents operator+operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(// use the Cents constructor and operator+(intint, , intint)) return Cents(c1.m_nCents + c2.m_nCents); } int main() { Cents cCents1(6); Cents cCents2(8); Cents cCentsSum = cCentscCents1 1 + cCents+ cCents22; std::cout << "I have " << cCentsSumcCentsSum ..GetCentsGetCents() () << " cents." ; }
When the operator does notnot modifymodify itsits operandsoperands, operator can overloading via friendfriend functionfunction. OverloadingOverloading operatorsoperators usingusing membermember functionsfunctions isis betterbetter optionoption
Overloading the arithmetic operators
46
class Cents { int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } friendfriend Cents operator+operator+(const Cents &c1, const Cents &c2); friendfriend Cents operatoroperator--(const Cents &c1, const Cents &c2); int GetCents() { return m_nCents; } }; // note: this function is not a member function!// note: this function is not a member function! Cents operator+operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(// use the Cents constructor and operator+(intint, , intint)) return Cents(c1.m_nCents + c2.m_nCents); } // note: this function is not a member function!// note: this function is not a member function! Cents operatoroperator--(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator// use the Cents constructor and operator--((intint, , intint)) return Cents(c1.m_nCents - c2.m_nCents); }
Overloading the multiplication operator and division operator are as easy as defining : •• operator*operator* •• operator/operator/
Overloading operators for operands of different types
47
class Cents { int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } // Overload // Overload cCentscCents + + intint friend Cents operator+(const Cents &cCents, intint nCentsnCents); // Overload // Overload intint + + cCentscCents friend Cents operator+(intint nCentsnCents, const Cents &cCents); int GetCents() { return m_nCents; } }; // note: this function is not a member function!// note: this function is not a member function! Cents operator+(const Cents &cCents, intint nCentsnCents) { return Cents(cCents.m_nCents + nCents); } // note: this function is not a member function!// note: this function is not a member function! Cents operator+(intint nCentsnCents, const Cents &cCents) { return Cents(cCents.m_nCents + nCents); }
Cents cCents c1 1 = Cents(= Cents(44) + ) + 66;; Cents cCents c2 2 = = 6 6 + Cents(+ Cents(44););
Overloading the I/O operators
48
class Point { double m_dX, m_dY, m_dZ; public: Point(double dX=0.0, double dY=0.0, double dZ=0.0) { m_dX = dX; m_dY = dY; m_dZ = dZ; } double GetX() { return m_dX; } double GetY() { return m_dY; } double GetZ() { return m_dZ; } }; int main() { Point cPoint(5.0, 6.0, 7.0); cout << "(" << cPoint.GetX() << ", " << cPoint.GetY() << ", " << cPoint.GetZ() << ")"; }
Point Point cPointcPoint((55..00, , 66..00, , 77..00);); cout << cout << cPointcPoint;;
Overloading the I/O operators
49
class Point { double m_dX, m_dY, m_dZ; public: Point(double dX=0.0, double dY=0.0, double dZ=0.0) { m_dX = dX; m_dY = dY; m_dZ = dZ; } friend friend ostreamostream& & operator<< (operator<< (ostreamostream &&out, out, Point &Point &cPointcPoint);); double GetX() { return m_dX; } double GetY() { return m_dY; } double GetZ() { return m_dZ; } }; ostreamostream& & operator<< (operator<< (ostreamostream &&out, out, Point &Point &cPointcPoint) ) { out << "(" << cPoint.m_dX << ", " << cPoint.m_dY << ", " << cPoint.m_dZ << ")"; return out;return out; }
Point cPoint1(2.0, 3.0, 4.0); Point cPoint2(6.0, 7.0, 8.0); coutcout << cPoint<< cPoint1 1 << " " << cPoint<< " " << cPoint2 2 << << endlendl;;
Overloading the I/O operators
50
class Point { double m_dX, m_dY, m_dZ; public: Point(double dX=0.0, double dY=0.0, double dZ=0.0) { m_dX = dX; m_dY = dY; m_dZ = dZ; } friendfriend istreamistream&& operator>> operator>> (istreamistream &&in, Point &Point &cPoint); double GetX() { return m_dX; } double GetY() { return m_dY; } double GetZ() { return m_dZ; } }; istreamistream&& operator>> operator>> (istreamistream &&in, Point &Point &cPoint) { in >> cPoint.m_dX; in >> cPoint.m_dY; in >> cPoint.m_dZ; return in;return in; }
Point cPoint; cincin >> >> cPointcPoint;; Point cPoint1, cPoint2; cincin >> cPoint>> cPoint1 1 >> cPoint>> cPoint22;;
Overloading the comparison operators
51
class Point { double m_dX, m_dY, m_dZ; public: Point(double dX=0.0, double dY=0.0, double dZ=0.0) { /* … */{ /* … */ }} friendfriend bool operator== operator== (Point &cP1, Point &cP2); friendfriend bool operator!= operator!= (Point &cP1, Point &cP2); friendfriend bool operator> operator> (Point &cP1, Point &cP2); friendfriend bool operator<= operator<= (Point &cP1, Point &cP2); friendfriend bool operator<operator< (Point &cP1, Point &cP2); friendfriend bool operator>= operator>= (Point &cP1, Point &cP2); /* … *//* … */ }; bool operator== operator== (Point &cP1, Point &cP2) { return (cP1.m_dX == cP2.m_dX && cP1.m_dY == cP2.m_dY && cP1.m_dZ == cP2.m_dZ); } bool operator!= operator!= (Point &cP1, Point &cP2) { return !(cP1 == cP2); } /* … *//* … */
Overloading unary operators
52
// positive (+), negative (// positive (+), negative (--) and logical not (!)) and logical not (!) class Point { double m_dX, m_dY, m_dZ; public: Point(double dX=0.0, double dY=0.0, double dZ=0.0) { /* … *//* … */ } friendfriend Point operatoroperator-- (const Point &cPoint); friendfriend bool operator!operator! (const Point &cPoint); /* … *//* … */ }; Point operatoroperator-- (const Point &cPoint) { return Point(-cPoint.m_dX, -cPoint.m_dY, -cPoint.m_dZ); } bool operator!operator! (const Point &cPoint){ return (cPoint.m_dX == 0.0 && cPoint.m_dY == 0.0 && cPoint.m_dZ == 0.0); }
Point cPoint; // use default // use default contructorcontructor to set to (to set to (00..00, , 00..00, , 00..00)) if (!if (!cPointcPoint) …) …
Overloading operators using member functions
• When overloading an operator using a member function: – The leftmostleftmost operandoperand of the overloaded operator mustmust bebe
anan objectobject ofof thethe classclass typetype – The leftmost operand leftmost operand becomes the implicit *this implicit *this
parameterparameter. All other operands become function All other operands become function parametersparameters
• Most operators can actually be overloaded either way, however there are a few exception few exception cases: –– If the leftmost operand is not a member of the class typeIf the leftmost operand is not a member of the class type
•• e.g., operator+(e.g., operator+(intint, , YourClassYourClass)), operator<<(operator<<(ostreamostream&, &, YourClassYourClass))
– The assignment (=)assignment (=), subscript ([])subscript ([]), call (())call (()), and member member selection (selection (-->)>) operators must be overloaded as member functions
53
Overloading operators using member functions
54
class Cents { private: int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } friend Cents operator-(const Cents &cCents); friend Cents operator+(Cents &cCents, int nCents); }; // note: this function is not a member function!// note: this function is not a member function! Cents operator-(const Cents &cCents) { return Cents(-cCents.m_nCents); } Cents operator+(Cents &cCents, int nCents) { return Cents(cCents.m_nCents + nCents); }
Overloading operators using member functions
55
class Cents { private: int m_nCents; public: Cents(int nCents) { m_nCents = nCents; } // Overload // Overload --cCentscCents : : --this this Cents operator-(); // Overload // Overload cCentscCents + + intint : this + : this + intint Cents operator+(int nCents); }; // note: the following functions are member function!// note: the following functions are member function! Cents CentsCents::operator::operator--() { return Cents(-m_nCents); } Cents CentsCents::operator+::operator+(int nCents) { return Cents(m_nCents + nCents); // this // this --> > m_nCentsm_nCents + …+ … }
Remember that C++ compiler internally converts Cents Cents CentsCents::operator::operator--(); (); to Cents operatorCents operator--(const Cents *this)(const Cents *this), which you will note is almost identical to our friend version Cents operator-(const Cents &cCents)!
Overloading prefix increment and decrement
56
// overloaded exactly the same as any normal unary operator// overloaded exactly the same as any normal unary operator class Digit { int m_nDigit; public: Digit(int nDigit=0) { m_nDigit = nDigit; } Digit& operator++(); Digit& operator--(); int GetDigit() const { return m_nDigit; } }; Digit& Digit::operator++Digit::operator++() { if (m_nDigit == 9) m_nDigit = 0; else ++m_nDigit; return *this; return *this; // Note that we return *this// Note that we return *this } Digit& Digit::operatorDigit::operator----() { if (m_nDigit == 0) m_nDigit = 9; else --m_nDigit; return *this; return *this; // Note that we return *this// Note that we return *this }
Digit d; cout<< (++++d).cout<< (++++d).GetDigitGetDigit(); (); // // 22
Overloading postfix increment and decrement
57
// uses a “dummy variable” or “dummy argument” for the postfix operators // uses a “dummy variable” or “dummy argument” for the postfix operators class Digit { int m_nDigit; public: Digit(int nDigit=0) { m_nDigit = nDigit; } Digit operator++operator++(intint); // postfix// postfix Digit operatoroperator----(intint); // postfix// postfix int GetDigit() const { return m_nDigit; } }; Digit DigitDigit::operator++::operator++(intint) { Digit cResult(m_nDigitm_nDigit); // a temporary variable with our current digit// a temporary variable with our current digit ++(*this);++(*this); // Use prefix operator to increment this digit// Use prefix operator to increment this digit return cResult; // return temporary result// return temporary result } Digit DigitDigit::operator::operator----(intint) { Digit cResult(m_nDigitm_nDigit); ----(*this);(*this); return cResult; }
Digit cDigit(5); ++++cDigitcDigit; ; // calls Digit::operator++();// calls Digit::operator++(); cDigitcDigit++; ++; // calls Digit::operator++(// calls Digit::operator++(intint););
Overloading the subscript operator
58
class IntList { private: int m_anList[10]; public: void SetItem(int nIndex, int nData) { m_anList[nIndex] = nData; } int GetItem(int nIndex) { return m_anList[nIndex]; } }; int main() { IntList cMyList; cMyList.SetItemcMyList.SetItem((22, , 33);); return 0; }
Overloading the subscript operator
59
class IntList { private: int m_anList[10]; public: intint& & operator[]operator[] (const const intint nIndex); }; intint& & IntListIntList::::operator[]operator[] (const const intint nIndex) { return m_anList[nIndex]; } int main() { IntList cMyList; cMyListcMyList[[22] = ] = 33; ; // set a value// set a value cout << cMyListcMyList[[22]]; // get a value// get a value }
Why operator[] returns a reference?Why operator[] returns a reference?
Overloading the subscript operator
60
#include <cassert> // for assert()// for assert() class IntList { private: int m_anList[10]; public: intint& & operator[]operator[] (const const intint nIndex); }; intint& & IntListIntList::::operator[]operator[] (const const intint nIndex) { assert(assert(nIndexnIndex >= >= 0 0 && && nIndexnIndex < < 1010);); return m_anList[nIndex]; }
int anArray[5]; anArray[7] = 3; // index // index 7 7 is out of bounds!is out of bounds!
Overloading the parenthesis operator
61
#include <cassert> // for assert()// for assert() class Matrix { double adData[4][4]; public: Matrix() { for (int nCol=0; nCol<4; nCol++) for (int nRow=0; nRow<4; nRow++) adData[nRow][nCol] = 0.0; } double& operator()operator()(const int nCol, const int nRow); void operator()operator()(); }; double& Matrix::operator()Matrix::operator()(const int nCol, const int nRow) { assert(assert(nColnCol >= >= 0 0 && && nColnCol < < 44); assert(); assert(nRownRow >= >= 0 0 && && nRownRow < < 44);); return adData[nRow][nCol]; } void Matrix::operator()()Matrix::operator()(){ for (int nCol=0; nCol<4; nCol++) for (int nRow=0; nRow<4; nRow++) adData[nRow][nCol] = 0.0; }
Matrix cMatrix; cMatrixcMatrix((11, , 22) = ) = 44..55;; std::cout << cMatrixcMatrix((11, , 22);); // // 44..55 cMatrixcMatrix(); (); // // eraseerase cMatrixcMatrix std::cout << cMatrixcMatrix((11, , 22);); // // 00
Overloading typecasts
62
class Cents { int m_nCents; public: Cents(int nCents=0) { m_nCents = nCents; } int GetCents() { return m_nCents; } void SetCents(int nCents) { m_nCents = nCents; } }; void PrintInt(int nValue) { cout << nValue; } int main() { Cents cCents(7); PrintInt(cCents.GetCentscCents.GetCents()()); }
int nValue = 5; // // intint implicitly cast to a doubleimplicitly cast to a double double dValue = nValue; // our goal// our goal Cents cCents(7); PrintInt(cCentscCents); // // printprint 77 int nCents = static_caststatic_cast<<intint>(>(cCentscCents); ); PrintInt(nCents); // // printprint 77
Overloading typecasts
63
/* Casting operators do not have a return type. C++ assumes you will be /* Casting operators do not have a return type. C++ assumes you will be returning the correct type */returning the correct type */ class Cents { private: int m_nCents; public: Cents(int nCents=0) { m_nCents = nCents; } // Overloaded // Overloaded intint castcast
operatoroperator intint() { return m_nCents; }
int GetCents() { return m_nCents; } void SetCents(int nCents) { m_nCents = nCents; } }; void PrintInt(intint nValue) { cout << nValue; }
Cents cCents(7);
PrintIntPrintInt((cCentscCents); ); // // printprint 77 intint nCentsnCents = = static_caststatic_cast<<intint>(>(cCentscCents););
PrintInt(nCents); // // printprint 77
Overloading typecasts
64
class Cents class Cents { int m_nCents; public: /* … */ int GetCents() { return m_nCents; } }; ////////////////////////////////////////////////////////////////////////// class Dollars class Dollars { int m_nDollars; public: Dollars(int nDollars=0) { m_nDollars = nDollars; } // Allow us to convert Dollars into Cents// Allow us to convert Dollars into Cents operatoroperator CentsCents() { return Cents(m_nDollars * 100); } }; voidvoid PrintCentsPrintCents((Cents Cents cCentscCents)) { cout << cCents.GetCents(); }
Dollars cDollars(9); // // cDollarscDollars will be cast to a Centswill be cast to a Cents PrintCents(cDollarscDollars);
copy constructor and overloading assignment operator
65
class Cents { int m_nCents; public: Cents(intint nCentsnCents==00) { m_nCents = nCents; } // Copy constructor// Copy constructor Cents(const Cents &const Cents &cSource) { m_nCents = cSource.m_nCents; } Cents& operator= operator= (const Cents &cSource); }; Cents& Cents::operator= Cents::operator= (const Cents &cSource) { // do the copy// do the copy m_nCents = cSource.m_nCents; // return the existing object// return the existing object return *this; return *this; // // cMarkcMark = = cNancycNancy = = cFredcFred = = cJoecJoe;; }
Cents cMark(5); // calls Cents constructor// calls Cents constructor Cents cNancy; // calls Cents default constructor// calls Cents default constructor cNancy = cMark; // calls Cents assignment operator// calls Cents assignment operator // note the following line// note the following line Cents cNancy = cMark; // calls Cents copy constructor!// calls Cents copy constructor!
copy constructor and overloading assignment operator
• The assignmentassignment operatoroperator is used to copy the values from one object to another already existing object. The key words here are alreadyalready existingexisting – Cents cMark(5); // calls Cents constructor// calls Cents constructor – Cents cNancy; // calls Cents default constructor// calls Cents default constructor – cNancy = cMark; // calls Cents assignment operator// calls Cents assignment operator
• A copycopy constructorconstructor is a special constructor that
initializes aa newnew objectobject fromfrom anan existingexisting objectobject – Cents cNancy = cMark; // calls Cents copy constructor!// calls Cents copy constructor!
– Because the secondsecond statementstatement usesuses anan equalsequals symbolsymbol inin
it,it, youyou mightmight expectexpect thatthat itit callscalls thethe assignmentassignment operatoroperator.. However, it doesn’t! It actually calls a special type of constructor calledcalled aa copycopy constructorconstructor
66
copy constructor and overloading assignment operator
• It is possible in C++ to do a selfself--assignmentassignment:
– cMark = cMark; //// validvalid assignmentassignment
•• toto dodo aa checkcheck forfor selfself--assignmentassignment atat thethe toptop ofof anan overloadedoverloaded assignmentassignment operatoroperator
67
Cents& Cents::operator=Cents::operator= (const Cents &cSource) { // check for self// check for self--assignment by comparing the address of theassignment by comparing the address of the ifif (this == &(this == &cSourcecSource) ) returnreturn *this;*this; m_nCents = cSource.m_nCents; return *this; }
copy constructor and overloading assignment operator
• Just like defaultdefault constructorconstructor, C++ will provide a defaultdefault copycopy constructorconstructor if you do not provide one yourself. However, unlike other operators, C++ will provide a defaultdefault assignmentassignment operatoroperator if you do not provide one yourself!
• Because C++ does not know much about your class, the defaultdefault copycopy constructorconstructor andand defaultdefault assignmentassignment operatorsoperators itit providesprovides areare veryvery simplesimple. They use a copying method known as a memberwisememberwise copycopy (also known as a shallowshallow copycopy).
68
Shallow copying
• A shallowshallow copycopy means that C++ copies each member of the class individually usingusing thethe assignmentassignment operatoroperator. When classes are simple (eg. do not contain any dynamically allocated memory), this works very well:
69
class Cents { private: int m_nCents; public: Cents(int nCents=0) { m_nCents = nCents; } };
Shallow copying
70
class class MyStringMyString { char *char *m_pchString; int m_nLength; public: MyString(char *pchString="") // default constructor// default constructor { // Find the length of the string plus one character for a terminator// Find the length of the string plus one character for a terminator m_nLength = strlen(pchString) + 1; m_pchString= new char[new char[m_nLengthm_nLength];]; // Allocate a buffer equal to this length// Allocate a buffer equal to this length strncpy(m_pchString, pchString, m_nLength); m_pchString[m_nLength-1] = '\0'; // Make sure the string is terminated// Make sure the string is terminated } ~MyString() { // destructor // destructor delete[] delete[] m_pchStringm_pchString; ; // We need to // We need to deallocatedeallocate our bufferour buffer m_pchStringm_pchString = = 00; ; // Set // Set m_pchStringm_pchString to null just in caseto null just in case } char* GetString() { return m_pchString; } int GetLength() { return m_nLength; } };
MyString cHello("Hello, world!"); { MyString cCopy = cHello; // use default copy constructor// use default copy constructor } // // cCopycCopy goes out of scope heregoes out of scope here
std::cout << cHello.GetString(); // this will crash// this will crash
Shallow copying • This (MyStringMyString cCopycCopy == cHellocHello;;) seems harmless
enough as well, but it’s actually thethe sourcesource ofof ourour problem!problem!
• When this line is evaluated, C++ will useuse thethe defaultdefault copycopy constructorconstructor – because we haven’t provided our ownwe haven’t provided our own
– Because aa shallowshallow pointerpointer copycopy justjust copiescopies thethe addressaddress ofof
thethe pointerpointer
– the addressaddress ofof cHellocHello..m_pchStringm_pchString isis copiedcopied intointo cCopycCopy..m_pchStringm_pchString. As a result, cCopy.m_pchString and cHello.m_pchString are nownow bothboth pointingpointing toto thethe samesame piecepiece ofof memory!memory!
71
Shallow copying
• consider the following line –– }} // // cCopycCopy goes out of scope heregoes out of scope here
• When cCopycCopy goesgoes outout ofof scopescope, the MyString destructordestructor isis calledcalled onon cCopycCopy – The destructor deletes the dynamicallydynamically allocatedallocated
memorymemory thatthat bothboth cCopycCopy..m_pchStringm_pchString andand cHellocHello..m_pchStringm_pchString areare pointingpointing to!to!
– Consequently, by deleting cCopy, we’ve also (inadvertently) affected cHello. Note that the destructor will set cCopy.m_pchString to 0, but cHellocHello..m_pchStringm_pchString willwill bebe leftleft pointingpointing toto thethe deleteddeleted (invalid)(invalid) memory!memory!
72
Deep copying
73
class MyString { / * … */
public: // Copy constructor// Copy constructor
MyString(const const MyStringMyString& & cSource) { // because // because m_nLengthm_nLength is not a pointer, we can shallow copy itis not a pointer, we can shallow copy it m_nLength = cSource.m_nLength; // // m_pchStringm_pchString is a pointer, so we need to deep copy it if it is nonis a pointer, so we need to deep copy it if it is non--nullnull if (if (cSource.m_pchStringcSource.m_pchString)) { // allocate memory for our copy// allocate memory for our copy m_pchString = new char[new char[m_nLengthm_nLength]]; // Copy the string into our newly allocated memory// Copy the string into our newly allocated memory strncpy(m_pchString, cSource.m_pchString, m_nLength); } else m_pchStringm_pchString = = 00;; }
};
MyString cHello("Hello, world!"); { MyString cCopy = cHello; // use copy constructor// use copy constructor } // // cCopycCopy goes out of scope heregoes out of scope here std::cout << cHello.GetString();
Deep copying
• What happens when we do the following?
–– MyStringMyString cHellocHello("Hello, world!");("Hello, world!");
–– cHellocHello = = cHellocHello; ; // calls assignment operator// calls assignment operator
74
// Problematic assignment operator// Problematic assignment operator MyString& MyStringMyString::operator=::operator=(const MyString& cSource) {
// Note: No check for self// Note: No check for self--assignment!assignment! delete[] delete[] m_pchString; // // deallocatedeallocate any value that this string is holding!any value that this string is holding! // because // because m_nLengthm_nLength is not a pointer, we can shallow copy itis not a pointer, we can shallow copy it m_nLength = cSource.m_nLength; if (cSource.m_pchString) { m_pchString = new char[new char[m_nLengthm_nLength];]; // allocate memory for our copy// allocate memory for our copy strncpy(m_pchString, cSource.m_pchString, m_nLength); } else m_pchString = 0; return *this;return *this; }
Deep copying
• What happens when we do the following?
–– MyStringMyString cHellocHello("Hello, world!");("Hello, world!");
–– cHellocHello = = cHellocHello; ; // calls assignment operator// calls assignment operator
75
// Assignment operator// Assignment operator MyString& MyStringMyString::operator=::operator=(const MyString& cSource) {
if (this == &this == &cSourcecSource) return *this; // check for self// check for self--assignmentassignment delete[] m_pchString; // // deallocatedeallocate any value that this string is holding!any value that this string is holding! // because // because m_nLengthm_nLength is not a pointer, we can shallow copy itis not a pointer, we can shallow copy it m_nLength = cSource.m_nLength; // now we need to deep copy // now we need to deep copy m_pchStringm_pchString if (cSource.m_pchString) { m_pchString = new char[m_nLength]; // allocate memory for our copy// allocate memory for our copy strncpy(m_pchString, cSource.m_pchString, m_nLength); } else m_pchString = 0; return *this; }
Preventing copying
• C++ will not automatically create a default copy constructor and default assignment operator, because we’ve told the compiler we’re defining our own functions
76
class MyString { private:private: char *m_pchString; int m_nLength;
MyStringMyString(const (const MyStringMyString& & cSourcecSource);); MyStringMyString& & operator=operator=(const (const MyStringMyString& & cSourcecSource);); public: /*…*/ };
Constructor initialization lists
77
class Something { private: int m_nValue; double m_dValue; int *m_pnValue; public: Something() { m_nValuem_nValue = = 00;; m_dValuem_dValue = = 00..00;; m_pnValuem_pnValue = = 00;; } };
class Something { private: int m_nValue; double m_dValue; int *m_pnValue; public: Something() : : m_nValuem_nValue((00), ), m_dValuem_dValue((00..00), ), m_pnValuem_pnValue((00)) { } };
Constructor initialization lists
78
class Something { private: constconst int m_nValue; public: Something() { m_nValuem_nValue = = 55;; } };
class Something { private: constconst int m_nValue; public: Something() : : m_nValuem_nValue((55)) { } };
Aggregation
• A special form of association that specifies a wholewhole--partpart relationshiprelationship between the aggregate (whole) and a component part
•• nono ownershipownership betweenbetween thethe complexcomplex objectobject andand thethe subobjectssubobjects is implied
• When an aggregateaggregate isis destroyeddestroyed, the subobjectssubobjects areare notnot destroyeddestroyed
79
Constructor initialization lists
80
class Teacher { private: string m_strName; public: Teacher(string strName) : m_strName(strName) { } string GetName() { return m_strName; } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Department { private: Teacher *m_pcTeacher; // // This dept holds only one teacherThis dept holds only one teacher public: Department(Teacher *pcTeacher=NULL) : m_pcTeacher(pcTeacher) { } };
Aggregation
81
class Teacher { private: string m_strName; public: Teacher(string strName) : m_strName(strName) { } string GetName() { return m_strName; } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Department { private: Teacher *m_pcTeacher; // This dept holds only one teacher// This dept holds only one teacher public: Department(Teacher *pcTeacher=NULL) : m_pcTeacher(pcTeacher) { } };
// // Create a teacher outside the scope of the DepartmentCreate a teacher outside the scope of the Department Teacher *pTeacher = new Teacher("Bob"); // create a teacher { Department cDept(pTeacher); } // // cDeptcDept goes out of scope here and is destroyedgoes out of scope here and is destroyed // // pTeacherpTeacher still exists here because still exists here because cDeptcDept did not destroy itdid not destroy it delete pTeacher;
Composition
•• compositioncomposition:: building complex objects from simpler objects – you have a head, a body, some legs, arms, and …
• A form of aggregationaggregation with strongstrong ownershipownership and coincidentcoincident lifetimelifetime as part of the whole – A personal computer hashas--aa CPU, a motherboard, and
other components
• has a strong life cycle dependencylife cycle dependency –– compositioncomposition isis destroyeddestroyed, all of the subobjectssubobjects areare
destroyeddestroyed asas wellwell
82
Composition
83
#include "#include "CPU.hCPU.h"" #include "#include "Motherboard.hMotherboard.h"" #include "#include "RAM.hRAM.h"" class PC { private: CPU CPU m_cCPUm_cCPU;; Motherboard Motherboard m_cMotherboardm_cMotherboard;; RAM RAM m_cRAMm_cRAM;; }; PC::PersonalComputer( int nCPUSpeed, char *strMotherboardModel, int nRAMSize) : : m_cCPUm_cCPU((nCPUSpeednCPUSpeed), ), m_cMotherboardm_cMotherboard((strMotherboardModelstrMotherboardModel), ), m_cRAMm_cRAM((nRAMSizenRAMSize)) { }
Relationships
84
Introduction to inheritance
•• InheritanceInheritance involves creating new objects by directly acquiring the attributes and behaviors of other objects and then extendingextending oror specializingspecializing themthem
• an isis--aa relationshiprelationship between two classes
– C++ inherited many features from C
85
Fruit
Banana Apple
Shape
Triangle Rectangle
parentparent or basebase
childchild or derivedderived
Liskov substitution principle
• introduced by BarbaraBarbara LiskovLiskov in a 1987 –– SubstitutabilitySubstitutability is a principle in object-oriented
programming
• if SS isis aa subtypesubtype ofof T,T, thenthen objectsobjects ofof typetype TT maymay bebe replacedreplaced withwith objectsobjects ofof typetype SS without altering any of the desirable properties of that program –– PreconditionsPreconditions cannot be strengthenedstrengthened inin aa subtypesubtype
–– PostconditionsPostconditions cannot be weakenedweakened inin aa subtypesubtype
–– InvariantsInvariants of the supertype mustmust bebe preservedpreserved inin aa subtypesubtype
86
Inheritance
87
class PersonPerson { public: string m_strName; int m_nAge; bool m_bIsMale; string GetName() { return m_strName; } int GetAge() { return m_nAge; } bool IsMale() { return m_bIsMale; } Person(string strName = "", int nAge = 0, bool bIsMale = false) : m_strName(strName), m_nAge(nAge), m_bIsMale(bIsMale) { } }; // // BaseballPlayerBaseballPlayer publicly inheriting Personpublicly inheriting Person
class BaseballPlayer : public Person public Person { public: double m_dBattingAverage; int m_nHomeRuns; BaseballPlayer(double dBattingAverage = 0.0, int nHomeRuns = 0) : m_dBattingAverage(dBattingAverage), m_nHomeRuns(nHomeRuns) { } };
Inheritance
88
class PersonPerson { public: string m_strName; int m_nAge; bool m_bIsMale; string GetName() { return m_strName; } int GetAge() { return m_nAge; } bool IsMale() { return m_bIsMale; } Person(string strName = "", int nAge = 0, bool bIsMale = false) : m_strName(strName), m_nAge(nAge), m_bIsMale(bIsMale) { } }; // // BaseballPlayerBaseballPlayer publicly inheriting Personpublicly inheriting Person
class BaseballPlayer : public Person public Person { public: double m_dBattingAverage; int m_nHomeRuns; BaseballPlayer(double dBattingAverage = 0.0, int nHomeRuns = 0) : m_dBattingAverage(dBattingAverage), m_nHomeRuns(nHomeRuns) { } };
// Create a new // Create a new BaseballPlayerBaseballPlayer objectobject BaseballPlayer cJoe; cJoe.m_strNamem_strName = "Joe"; // Print out the name// Print out the name cout << cJoe.GetNameGetName() << std::endl;
Inheritance
89
class Employee { public: string m_strEmployerName; double m_dHourlySalary; long m_lEmployeeID; Employee(string strEmployerName, double dHourlySalary, long lEmployeeID) : m_strEmployerName(strEmployerName), m_dHourlySalary(dHourlySalary), m_lEmployeeID(lEmployeeID) { } double GetHourlySalary() { return m_dHourlySalary; } void PrintNameAndSalary() { cout << m_strName << ": " << m_dHourlySalary << std::endl; } };
class Supervisor: public Employee: public Employee { public: // This Supervisor can oversee a max of // This Supervisor can oversee a max of 5 5 employeesemployees int m_nOverseesIDs[5]; };
Constructors and initialization of derived classes
90
class Base { public: int m_nValue; Base(int nValue=0) : m_nValue(nValue) { } };
class Derived: public Base: public Base { public: double m_dValue; Derived(double dValue=0.0) : m_dValue(dValue) { } };
Memory for cBase is set aside The appropriate Base constructor is called The initialization list initializes variables The body of the constructor executes Control is returned to the caller Memory for cDerived is set aside (enough for both the Base and Derived portions). The appropriate Derived constructor is called The Base object is constructed first using the appropriate Base constructor The initialization list initializes variables The body of the constructor executes Control is returned to the caller
int main() { Base cBase(5); Derived cDerived(1.3); }
Constructors and initialization of derived classes
91
class Base { public: int m_nValue; Base(int nValue=0) : m_nValue(nValue) { } };
class Derived: : public Base public Base { public: double m_dValue; Derived(double dValue=0.0, int nValue=0) // // does not workdoes not work : m_dValue(dValue), m_nValue(nValue) { } };
C++ preventsprevents classesclasses fromfrom initializinginitializing inheritedinherited membermember variablesvariables inin thethe initializationinitialization
listlist ofof aa constructorconstructor. Why? The answer has to do with constconst andand referencereference variablesvariables. Consider what would happen if m_nValue were const. BecauseBecause constconst variablesvariables mustmust bebe initializedinitialized withwith aa valuevalue atat thethe timetime ofof creation,creation, thethe basebase classclass constructorconstructor mustmust setset it’sit’s valuevalue whenwhen thethe variablevariable isis createdcreated.. However,However, whenwhen thethe basebase classclass constructorconstructor finishes,finishes, thethe derivedderived classclass constructorsconstructors initializationinitialization listslists areare thenthen executedexecuted.. EachEach derivedderived classclass wouldwould thenthen havehave thethe opportunityopportunity toto initializeinitialize thatthat variable,variable, potentiallypotentially changingchanging it’sit’s value!value! ByBy restrictingrestricting thethe initializationinitialization ofof variablesvariables toto thethe constructorconstructor ofof thethe classclass thosethose variablesvariables belongbelong to,to, C++C++ ensuresensures thatthat allall variablesvariables areare initializedinitialized onlyonly onceonce. The end result is that the above example does not work because m_nValue was inherited from Base, and only non-inherited variables can be changed in the initialization list.
Constructors and initialization of derived classes
• When a derivedderived classclass isis destroyeddestroyed, eacheach destructordestructor isis calledcalled inin thethe reversereverse orderorder ofof constructionconstruction
92
class Base { public: int m_nValue; Base(int nValue=0) : m_nValue(nValue) { } };
class Derived: public Base : public Base { public: double m_dValue; Derived(double dValue=0.0, int nValue=0) // Call Base(// Call Base(intint) constructor with value ) constructor with value nValuenValue!!
: Base(: Base(nValuenValue)), m_dValue(dValue)
{ } };
Inheritance and access specifiers
93
class Base { public: // can be accessed by anybody// can be accessed by anybody int m_nPublic; private: // can only be accessed by Base member functions (but not derived classes)// can only be accessed by Base member functions (but not derived classes) int m_nPrivate; protected: // can be accessed by Base member functions, or derived classes// can be accessed by Base member functions, or derived classes int m_nProtected; };
class Derived: public Base : public Base { public: Derived() { m_nPublicm_nPublic = 1; // allowed: can access public base members from derived class// allowed: can access public base members from derived class m_nPrivatem_nPrivate = 2; // not allowed: can not access private base members from derived class// not allowed: can not access private base members from derived class m_nProtectedm_nProtected = 3; // allowed: can access protected base members from derived class// allowed: can access protected base members from derived class } };
Inheritance and access specifiers
94
class Base { public: // can be accessed by anybody// can be accessed by anybody int m_nPublic; private: // can only be accessed by Base member functions (but not derived classes)// can only be accessed by Base member functions (but not derived classes) int m_nPrivate; protected: // can be accessed by Base member functions, or derived classes// can be accessed by Base member functions, or derived classes int m_nProtected; };
class Derived: public Base : public Base { public: Derived() { m_nPublicm_nPublic = 1; // allowed: can access public base members from derived class// allowed: can access public base members from derived class m_nPrivatem_nPrivate = 2; // not allowed: can not access private base members from derived class// not allowed: can not access private base members from derived class m_nProtectedm_nProtected = 3; // allowed: can access protected base members from derived class// allowed: can access protected base members from derived class } };
int main() { Base cBase; cBase.m_nPublicm_nPublic = 1; // allowed// allowed cBase.m_nPrivatem_nPrivate = 2; // not allowed// not allowed cBase.m_nProtectedm_nProtected = 3; // not allowed// not allowed }
Inheritance and access specifiers
95
// A class can always access it’s own members regardless of access // A class can always access it’s own members regardless of access specifierspecifier // Inherit from Base publicly// Inherit from Base publicly class Pub: public Base: public Base { }; // Inherit from Base privately// Inherit from Base privately class Pri: private Base: private Base { }; // Inherit from Base // Inherit from Base protectedlyprotectedly class Pro: protected Base: protected Base { }; class Def: Base : Base // Defaults to private inheritance// Defaults to private inheritance { };
public access specifiers
96
class Base { public: int m_nPublic; private: int m_nPrivate; protected: int m_nProtected; }; // Public inheritance means:// Public inheritance means: // // m_nPublicm_nPublic stays publicstays public // // m_nPrivatem_nPrivate stays privatestays private // // m_nProtectedm_nProtected stays protectedstays protected class Pub: public Base { Pub() { m_nPublicm_nPublic = 1; // ok: anybody can access public members// ok: anybody can access public members m_nPrivatem_nPrivate = 2; // Error : derived classes can't access private members in the base class!// Error : derived classes can't access private members in the base class! m_nProtectedm_nProtected = 3; // ok: derived classes can access protected members// ok: derived classes can access protected members } };
public inheritance is also the easiest to understand. When you inherit a base class publicly, all members keep their original access specifications int main(){ Pub cPub; cPub.m_nPublicm_nPublic = 1; // ok// ok cPub.m_nPrivatem_nPrivate = 2; // error// error cPub.m_nProtectedm_nProtected = 3; // error// error }
private access specifiers
97
class Base { public: int m_nPublic; private: int m_nPrivate; protected: int m_nProtected; }; // Private inheritance means:// Private inheritance means: // // m_nPublicm_nPublic becomes privatebecomes private // // m_nProtectedm_nProtected becomes privatebecomes private
class Pri : private Base : private Base { Pri() { m_nPublicm_nPublic = 1; // ok: anybody can access public members// ok: anybody can access public members m_nPrivatem_nPrivate = 2; // Error : derived classes can't access private members in the base class!// Error : derived classes can't access private members in the base class! m_nProtectedm_nProtected = 3; // ok: derived classes can access protected members// ok: derived classes can access protected members } };
With private inheritance, all members from the base class are inherited as private. This means private members stay private, and protected and public members become private int main(){ Pri cPri; cPri.m_nPublic = 1; // error// error cPri.m_nPrivate = 2; // error// error cPri.m_nProtected = 3; // error// error }
protected access specifiers
98
class Base { public: int m_nPublic; private: int m_nPrivate; protected: int m_nProtected; }; // protected inheritance means:// protected inheritance means: // // m_nPublicm_nPublic becomes protected becomes protected // // m_nProtectedm_nProtected becomes protected becomes protected class Pro : protected Base : protected Base { Pro() { m_nPublicm_nPublic = 1; // ok: anybody can access public members// ok: anybody can access public members m_nPrivatem_nPrivate = 2; // Error : derived classes can't access private members in the base class!// Error : derived classes can't access private members in the base class! m_nProtectedm_nProtected = 3; // ok: derived classes can access protected members// ok: derived classes can access protected members } };
With protected inheritance, the public and protected members become protected, and private members stay private int main(){ Pro cPro; cPro.m_nPublicm_nPublic = 1; // error// error cPro.m_nPrivatem_nPrivate = 2; // error// error cPro.m_nProtectedm_nProtected = 3; // error// error }
Adding members in a derived class
99
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { } void Identify() { cout << "I am a Base"; } };
class Derived: public Base : public Base { public: Derived(int nValue) :Base(nValue) { } int GetValue() { return m_nValue; } // Adding function member// Adding function member };
Derived cDerivedcDerived(5); cout << cDerived.GetValue(); // // 55 Base cBasecBase(5); cout << cBase.GetValueGetValue(); // Error// Error cBase.Identify(); // I am a Base// I am a Base cDerived.Identify(); // I am a Base// I am a Base
Redefining functionality in a derived class
100
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { } void Identify() { cout << "I am a Base "; } };
class Derived: public Base: public Base { public: Derived(int nValue) :Base(nValue) { } int GetValue() { return m_nValue; } // Adding function member// Adding function member // Here's our modified function// Here's our modified function void Identify() { cout << "I am a Derived"; } };
Derived cDerived(5); Base cBase(5); cBase.IdentifyIdentify(); // I am a Base cDerived.IdentifyIdentify(); // I am a Derived
Adding to existing functionality in a derived class
101
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { } void Identify() { cout << "I am a Base "; } };
class Derived: public Base: public Base { public: Derived(int nValue) :Base(nValue) { } int GetValue() { return m_nValue; } // Adding function member// Adding function member // Here's our modified function// Here's our modified function void Identify() {
Base::Identify()Base::Identify(); // if one write Identify(); then an infinite loop! // if one write Identify(); then an infinite loop! cout << "I am a Derived"; } };
Derived cDerived(5); Base cBase(5); cBase.IdentifyIdentify(); // I am a Base// I am a Base cDerived.IdentifyIdentify(); // I am a Base I am a Derived// I am a Base I am a Derived
Hiding existing functionality in a derived class
102
class Base { public: Base(int nValue) : m_nValue(nValue) { } int m_nValue; protected: void PrintValue() { cout << m_nValue; } }; class Derived: public Base: public Base {
Base::Base::m_nValuem_nValue;; public: Derived(int nValue) : Base(nValue) { } // Base::// Base::PrintValuePrintValue was inherited as protected, so the public has no accesswas inherited as protected, so the public has no access // But we're changing it to public by declaring it in the public section// But we're changing it to public by declaring it in the public section
Base::Base::PrintValuePrintValue;; };
Derived cDerived(7); /* The following won't work because /* The following won't work because m_nValuem_nValue has been redefined as private */has been redefined as private */ cout << cDerived.m_nValuem_nValue; // error// error // // PrintValuePrintValue is public in Derived, so this is okis public in Derived, so this is ok cDerived.PrintValuePrintValue(); // prints // prints 77
Multiple inheritance
103
class Scanner { }; class Printer { };
class Copier: public Scanner, public Printer: public Scanner, public Printer { };
Problems with multiple inheritance
104
class USBDevice { long m_lID; public: USBDevice(long lID) : m_lID(lID) { } long GetID() { return m_lID; } }; class NetworkDevice { long m_lID; public: NetworkDevice(long lID) : m_lID(lID) { } long GetID() { return m_lID; } }; class WirelessAdaptor: public : public USBDeviceUSBDevice, public , public NetworkDeviceNetworkDevice { public: WirelessAdaptor(long lUSBID, long lNetworkID) : USBDeviceUSBDevice((lUSBIDlUSBID), ), NetworkDeviceNetworkDevice((lNetworkIDlNetworkID)) { } };
WirelessAdaptor c54G(5442, 181742); cout << c54G.GetIDGetID(); // Which // Which GetIDGetID() do we call?() do we call? /* this function call is ambiguous, and you will receive /* this function call is ambiguous, and you will receive a compiler error if you try to compile it */a compiler error if you try to compile it */ cout << c54G.USBDevice::USBDevice::GetIDGetID();
diamond problem
105
/* There are many issues that arise in this context, including whether Copier should /* There are many issues that arise in this context, including whether Copier should have one or two copies of have one or two copies of PoweredDevicePoweredDevice, and how to resolve certain types of , and how to resolve certain types of ambiguous references */ambiguous references */ class PoweredDevice { }; class Scanner: public PoweredDevice { }; class Printer: public PoweredDevice { }; class Copier: public Scanner, public Printer { };
Problem Problem : diamond problemdiamond problem
solutionsolution : virtual inheritancevirtual inheritance
virtual inheritance
106
class PoweredDevicePoweredDevice { public: PoweredDevice(int nPower) { cout << "PoweredDevice: " << nPower << endl; } }; class ScannerScanner: public PoweredDevicePoweredDevice { public: Scanner(int nScanner, int nPower) : PoweredDevice(nPower) { cout << "Scanner: " << nScanner << endl; } }; class PrinterPrinter: public PoweredDevicePoweredDevice { public: Printer(int nPrinter, int nPower) : PoweredDevice(nPower) { cout << "Printer: " << nPrinter << endl; } }; class Copier: public Scanner, public Printer: public Scanner, public Printer { public: Copier(int nScanner, int nPrinter, int nPower) : Scanner(Scanner(nScannernScanner, , nPowernPower), Printer(), Printer(nPrinternPrinter, , nPowernPower)) { } };
Copier cCopier(1, 2, 3); PoweredDevice: 3 Scanner: 1 PoweredDevice: 3 Printer: 2
Scanner Printer
PoweredDevice PoweredDevice
Copier
PoweredDevice: 3 Scanner: 1 PoweredDevice: 3 Printer: 2
virtual inheritance
107
class PoweredDevicePoweredDevice { public: PoweredDevice(int nPower) { cout << "PoweredDevice: " << nPower << endl; } };
class Scanner: virtual public virtual public PoweredDevicePoweredDevice { public: Scanner(int nScanner, int nPower) : PoweredDevice(nPower) { cout << "Scanner: " << nScanner << endl; } };
class Printer: virtual public virtual public PoweredDevicePoweredDevice { public: Printer(int nPrinter, int nPower) : PoweredDevice(nPower) { cout << "Printer: " << nPrinter << endl; } }; class Copier: public Scanner, public Printer : public Scanner, public Printer { public: // The Copier constructor is responsible for creating // The Copier constructor is responsible for creating PoweredDevicePoweredDevice Copier(int nScanner, int nPrinter, int nPower) : Scanner(nScanner, nPower), Printer(nPrinter, nPower), PoweredDevicePoweredDevice((nPowernPower) ) { } };
Copier cCopier(1, 2, 3); PoweredDevice: 3 Scanner: 1 PoweredDevice: 3 Printer: 2
Scanner Printer
PoweredDevice
Copier
PoweredDevice: 3 Scanner: 1 Printer: 2
single inheritance
• Most of the problemsproblems that can be solved using multiplemultiple inheritanceinheritance can be solvedsolved using singlesingle inheritanceinheritance as well
•• ManyMany relativelyrelatively modernmodern languageslanguages such as Java and C# restrictsrestricts classesclasses toto singlesingle inheritanceinheritance of normal classes, but allow multiplemultiple inheritanceinheritance ofof interfaceinterface classesclasses (which we will talk about later)
108
Pointers and references to the base class of derived objects
109
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { } const char* GetName() { return "Base"; } int GetValue() { return m_nValue; } }; class Derived: : public Base public Base { public: Derived(int nValue) : Base(nValue) { } const char* GetName() { return "Derived"; } int GetValueDoubled() { return m_nValue * 2; } };
Derived cDerived(5); cout << "cDerived is a " << cDerived.GetName() << " and has value " << cDerived.GetValue() << endl; Derived &Derived &rDerived = cDerived; cout << "rDerived is a " << rDerived.GetName.GetName() << " and has value " << rDerived.GetValue.GetValue() << endl; Derived *Derived *pDerived = &cDerived; cout << "pDerived is a “ << pDerived-->>GetNameGetName() << " and has value “ << pDerived-->>GetValueGetValue() () << endl;
cDerived is a Derived and has value 5 rDerived is a Derived and has value 5 pDerived is a Derived and has value 5
Pointers and references to the base class of derived objects
110
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { } const char* GetName() { return "Base"; } int GetValue() { return m_nValue; } }; class Derived: public Base : public Base { public: Derived(int nValue) : Base(nValue) { } const char* GetName() { return "Derived"; } int GetValueDoubled() { return m_nValue * 2; } };
Derived cDerived(5); // These are both legal!// These are both legal! Base &Base &rBase = cDerived; Base *Base *pBase = &cDerived; cout << "cDerived is a “ << cDerived.GetName() << " and has value " << cDerived.GetValue() << endl; cout << "rBase is a " << rBase.GetName() << " and has value " << rBase.GetValue() << endl; cout << "pBase is a " << pBase->GetName() << " and has value " << pBase->GetValue() << endl;
cDerived is a Derived and has value 5
rBase is a BaseBase and has value 5
pBase is a BaseBase and has value 5
virtual function
111
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { }
virtual virtual const char* const char* GetName() { return "Base"; } int GetValue() { return m_nValue; } }; class Derived: public Base { public: Derived(int nValue) : Base(nValue) { }
virtual virtual const char* const char* GetName(){return "Derived"; } int GetValueDoubled() { return m_nValue * 2; } };
Derived cDerived(5); // // These are both legal!These are both legal! Base &Base &rBase = cDerived; Base *Base *pBase = &cDerived; cout << "cDerived is a “ << cDerived.GetName() << " and has value " << cDerived.GetValue() << endl; cout << "rBase is a " << rBase.GetName() << " and has value " << rBase.GetValue() << endl; cout << "pBase is a " << pBase->GetName() << " and has value " << pBase->GetValue() << endl;
cDerived is a Derived and has value 5
rBase is a Derived Derived and has value 5
pBase is a Derived Derived and has value 5
virtual function
112
class A { public: virtual const char* GetName() { return "A"; } }; class B: public A { public: virtual const char* GetName() { return "B"; } }; class C: public B { public: virtual const char* GetName() { return "C"; } }; class D: public C { public: virtual const char* GetName() { return "D"; } };
int main() { C cClass; A &rBase = cClass; cout << "rBase is a “ << rBase.GetName() << endl; return 0; }
rBase is a CC
Use of the virtual keyword
113
class Base { protected: int m_nValue; public: Base(int nValue) : m_nValue(nValue) { }
virtual const char* GetName() { return "Base"; } int GetValue() { return m_nValue; } }; class Derived: public Base { public: Derived(int nValue) : Base(nValue) { } // note lack of virtual keyword// note lack of virtual keyword const char* const char* GetNameGetName() { return "Derived"; }() { return "Derived"; } int GetValueDoubled() { return m_nValue * 2; } };
Exactly the same as if Derived::GetName() was explicitly tagged as virtual. Only the most base class function needs to be tagged as virtual for all of the derived functions to work virtually. However, having the keyword virtual on the derived functions does not hurt, and itit servesserves asas aa usefuluseful reminderreminder thatthat thethe functionfunction isis aa virtualvirtual functionfunction ratherrather thanthan aa normalnormal oneone. Consequently, it’s generally a good idea to use the virtual keyword for virtualized functions in derived classes even though it’s not strictly necessary
Return types of virtual functions
114
class Base { public:
virtualvirtual int GetValue() { return 5; } }; class Derived: public Base { public:
virtualvirtual double GetValue() { return 6.78; } };
Under normal circumstances, thethe returnreturn typetype ofof aa virtualvirtual functionfunction andand it’sit’s overrideoverride mustmust matchmatch.. Thus,Thus, thethe followingfollowing willwill notnot workwork
class Base { public:
virtualvirtual Base* GetThis() { return this; } }; class Derived: public Base { public:
virtualvirtual Derived* GetThis() { return this; } };
However, there is one special case in which this is not true.. IfIf thethe returnreturn typetype ofof aa virtualvirtual functionfunction isis aa pointerpointer oror aa referencereference toto aa class,class, overrideoverride functionsfunctions cancan returnreturn aa pointerpointer oror aa referencereference toto aa derivedderived classclass
• Note that some older compilers (eg. Visual Studio 6) do not support it
Example
115
class Animal { protected: string m_strName; Animal(string strName) : strName(strName) {} public: string GetName() { return m_strName; } virtual const char* virtual const char* Speak() { return "???"; } }; class Cat: public Animal { public: Cat(string strName) : Animal(strName) {} virtual const char* virtual const char* Speak() { return "Meow"; } }; class Dog: public Animal { public: Dog(string strName): Animal(strName) {} virtual const char* virtual const char* Speak() { return "Woof"; } };
We're makingmaking thisthis constructorconstructor protectedprotected becausebecause wewe don'tdon't wantwant peoplepeople creatingcreating AnimalAnimal objectsobjects directly,directly, but we still want derived classes to be able to use it
void Report(Animal &rAnimal) { cout << rAnimal.GetName() << " says " << rAnimal.Speak() << endl; } int main() { Cat cCat("Fred"); Dog cDog("Garbo"); Report(cCat); // Fred says Meow// Fred says Meow Report(cDog); // // GarboGarbo says Woofsays Woof }
Example
116
Cat acCats[] = { Cat("Fred"), Cat("Tyson"), Cat("Zeke") }; Dog acDogs[] = { Dog("Garbo"), Dog("Pooky"), Dog("Truffle") }; for (int i = 0; i < 3; i++) cout << acCats[i].GetName() << " says " << acCats[i].Speak() << endl; for (int I = 0; i < 3; i++) cout << acDogs[i].GetName() << " says " << acDogs[i].Speak() << endl;
Cat cFred("Fred"), cTyson("Tyson"), cZeke("Zeke"); Dog cGarbo("Garbo"), cPooky("Pooky"), cTruffle("Truffle"); // Set up an array of pointers to animals, and set those pointers to our Cat and Dog objects// Set up an array of pointers to animals, and set those pointers to our Cat and Dog objects Animal *apcAnimals[] = { &cFred, &cGarbo, &cPooky, &cTruffle, &cTyson, &cZeke }; for (int i = 0; i < 6; i++) cout << apcAnimals[i]->GetName() << " says " << apcAnimals[i]->Speak() << endl;
Virtual destructors
117
class Base { public: ~Base() { cout << "Calling ~Base()" << endl; } }; class Derived: public Base { int* m_pnArray; public: Derived(int nLength) { m_pnArray = new int[nLength]; } ~Derived() // note: not virtual// note: not virtual { cout << "Calling ~Derived()" << endl; delete[] m_pnArray; } };
Derived *pDerived = new Derived(5); Base *pBase = pDerived; delete pBase; // Calling ~Base()// Calling ~Base()
Virtual destructors
118
class Base { public:
virtual ~Base() { cout << "Calling ~Base()" << endl; } }; class Derived: public Base { int* m_pnArray; public: Derived(int nLength) { m_pnArray = new int[nLength]; }
virtual ~Derived() // note: virtual// note: virtual { cout << "Calling ~Derived()" << endl; delete[] m_pnArray; } };
Derived *pDerived = new Derived(5); Base *pBase = pDerived; delete pBase; // Calling ~Derived() // Calling ~Derived() // Calling ~Base()// Calling ~Base()
Overriding virtualization
119
// You probably won’t use this very often, but it’s good to know// You probably won’t use this very often, but it’s good to know class Base { public: virtual const char*virtual const char* GetName() { return "Base"; } }; class Derived: public Base { public virtual const char* virtual const char* GetName() { return "Derived"; } }; int main() { Derived cDerived; Base &rBase = cDerived; // Calls Base::GetName() instead of the virtualized Derived::GetName() cout << rBase.BaserBase.Base::::GetNameGetName() () << endl; }
Early binding • Binding refers to the process that is used toto convertconvert
identifiersidentifiers (such(such asas variablevariable andand functionfunction names)names) intointo machinemachine languagelanguage addressesaddresses. Although binding is used for both variables and functions, in this lesson we’re going to focus on function binding
•• EarlyEarly bindingbinding (also called staticstatic bindingbinding) meansmeans thethe compilercompiler isis ableable toto directlydirectly associateassociate thethe identifieridentifier namename (such(such asas aa functionfunction oror variablevariable name)name) withwith aa machinemachine addressaddress. Remember that all functions have a unique machine address. So when the compiler encounters a function call, it replaces the function call with a machine language instruction that tells the CPU to jump to the address of the function
120
Early binding
121
void PrintValue(int nValue) { cout << nValue; } int main() { PrintValuePrintValue((55)); // This is a direct function call// This is a direct function call return 0; }
Late Binding
•• late bindinglate binding or dynamic bindingdynamic binding
– function pointers
122
int Add(int nX, int nY) { return nX + nY; } int main() { // Create a function pointer and make it point to the Add function// Create a function pointer and make it point to the Add function intint (*(*pFcnpFcn)()(intint, , intint) = Add;) = Add; cout << pFcn(5, 3) << endl; // add // add 5 5 + + 33 return 0; }
The virtual table
• To implement virtual functions, C++ uses a special form of latelate bindingbinding knownknown asas thethe virtualvirtual tabletable
• The virtualvirtual tabletable isis aa lookuplookup tabletable ofof functionsfunctions used to resolveresolve functionfunction callscalls in a dynamic/late binding manner
– other names: vtablevtable,, virtualvirtual functionfunction table,table, virtualvirtual methodmethod table,table, dispatchdispatch tabletable
123
The virtual table class Baseclass Base { public: virtual void function1() {}; virtual void function2() {}; };
class Dclass D11: public Base: public Base { public: virtual void function1() {}; };
class Dclass D22: public Base: public Base { public: virtual void function2() {}; };
124
The virtual table – D1 cClass; – Base *pClass = &cClass; – pClass->function1();
• Because cClass is a D1 object, cClass has it’s *__vptr set to the D1 virtual table
• Note that because pClass is a base pointer, it only points to the Base portion of cClass. However, also note that *__vptr is in the Base portion of the class, so pClass has access to this pointer. Finally, note that pClass->__vptr points to the D1 virtual table! Consequently, even though pClass is of type Base, it still has access to D1′s virtual table
• First, the program recognizes that function1() is a virtual function. Second, uses pClass->__vptr to get to D1′s virtual table. Third, it looks up which version of function1() to call in D1′s virtual table. This has been set to D1::function1(). Therefore, pClass->function1() resolves to D1::function1()!
125
Pure virtual (abstract) functions
• C++ allows you to create a special kind of virtual function called a purepure virtualvirtual functionfunction (or(or abstractabstract function)function) thatthat hashas nono bodybody atat all!all! A pure virtual function simply actsacts asas aa placeholderplaceholder thatthat isis meantmeant toto bebe redefinedredefined byby derivedderived classesclasses
• When we add a pure virtual function to our class, we are effectively saying, “itit isis upup toto thethe derivedderived classesclasses toto implementimplement thisthis functionfunction”
126
Abstract classes
• Using a pure virtual function has two main consequences:
– First, any class with oneone oror moremore purepure virtualvirtual functionsfunctions becomesbecomes anan abstractabstract basebase classclass, which means that itit cancan notnot bebe instantiated!instantiated!
– Second, anyany derivedderived classclass mustmust definedefine aa bodybody forfor thisthis function,function, oror thatthat derivedderived classclass willwill bebe consideredconsidered anan abstractabstract basebase classclass asas wellwell
127
Abstract class
128
class Base { public: // a normal non// a normal non--virtual functionvirtual function const char* SayHi() { return "Hi"; } // a normal virtual function// a normal virtual function virtual const char* GetName() { return "Base"; } // a pure virtual function// a pure virtual function
virtual int GetValue() = = 00;
}; int main() { Base cBase; // Error// Error cBase.GetValue(); // what would this do?// what would this do? }
Abstract class
129
class Animal { protected: string m_strName; Animal(string strName) : m_strName(strName) {} public: string GetName() { return m_strName; } virtual const char* virtual const char* Speak() { return "???"; } }; class Cow: public Animal class Cow: public Animal { public: Cow(std::string strName): Animal(strName) { } // We forgot to redefine Speak// We forgot to redefine Speak };
class Animal { …
virtual const char* virtual const char* Speak() = = 00;
};
Interface classes
• An interfaceinterface classclass is a class that hashas nono membersmembers variables,variables, andand wherewhere allall ofof thethe functionsfunctions areare purepure virtual!virtual! In other words, the class is purely a definition, and has no actual implementation –– virtualvirtual destructordestructor oror purepure virtualvirtual functionsfunctions
• Interfaces are useful when you wantwant toto definedefine
thethe functionalityfunctionality thatthat derivedderived classesclasses mustmust implement,implement, butbut leaveleave thethe detailsdetails ofof howhow thethe derivedderived classclass implementsimplements thatthat functionalityfunctionality entirelyentirely upup toto thethe derivedderived classclass –– classclass DclassDclass :: publicpublic BclassBclass,, publicpublic InterfaceInterface11,, publicpublic
InterfaceInterface22,, ……
130
Interface classes
131
class class IErrorLogIErrorLog { virtual bool OpenLog(const char *strFilename) = = 00; virtual bool CloseLog() = = 00; virtual bool WriteError(const char *strErrorMessage) = = 00; }; class FileErrorLog : IErrorLogIErrorLog { // implementation of // implementation of IErrorLogIErrorLog } class ScreenErrorLog: IErrorLogIErrorLog { // implementation of // implementation of IErrorLogIErrorLog } double MySqrt(double dValue, ScreenErrorLog &cLog) { /*…*//*…*/ } double MySqrt(double dValue, FileErrorLog &cLog) { /*…*//*…*/ } double MySqrt(double dValue, IErrorLogIErrorLog &&cLog) { /*…*//*…*/} See Example of slide #115
Composition Instead Of Inheritance
•• IsIs--AA vs HasHas--aa
132
class MouthMouth { public: void eat(Food bite); };
// // A person is a mouthA person is a mouth class Person: public Mouth class Person: public Mouth { };
// A person has a mouth// A person has a mouth class PersonPerson { public: void eat(Food bite) { itsMouth.eat(bite);itsMouth.eat(bite); } private:private: Mouth Mouth itsMouthitsMouth;; };
Inheritance and static member functions
• static member act the same as non-static member: – They inherit into the derived class
– If you redefine a static member, all the other overloaded functions in the base class are hidden
– If you change the signature of a function in the base class, all the base class versions with that function name are hidden (this is really a variation of the previous point)
• However, staticstatic member functions cannot be virtualvirtual
133
Function templates - overview
134
int getMax(int nX, int nY) { return (nX > nY) ? nX : nY; } double getMax(double dX, double dY) { return (dX > dY) ? dX : dY; } // …// …
Function templates - overview
135
// this is the template parameter declaration// this is the template parameter declaration
templatetemplate <typenametypename TypeType> // templatetemplate <class Typeclass Type> TypeType getMax(TypeType dX, TypeType dY) { return (dX > dY) ? dX : dY; } // …// … intint nValue = getMaxgetMax((33, , 77); ); // returns // returns 77 doubledouble dValue = getMaxgetMax((66..3434, , 1818..523523);); // returns // returns 1818..523523 charchar chValue = getMaxgetMax('a', '('a', '66');'); // returns 'a'// returns 'a'
intint n2 = getMaxgetMax<<intint>(>(33, , 77); ); // returns // returns 77
doubledouble d2 = getMaxgetMax<<intint>(>(66..3434, , 1818..523523);); // returns // returns 1818
Function templates - overview
136
// this is the template parameter declaration// this is the template parameter declaration // template <class T, class U>// template <class T, class U> templatetemplate <typenametypename T, T, typenametypename UU>
TT max(TT dX, UU dY)
{ return (dX > dY) ? dX : dY; } // …// … intint nValue = max(max(33, , 77); ); // returns // returns 77 doubledouble dValue = max(max(66..3434, , 1818..523523);); // returns // returns 1818..523523 charchar chValue = max('a', 'max('a', '66');'); // returns 'a'// returns 'a'
Function templates - overview
137
templatetemplate <typenametypename TypeType> TypeType getMax(TypeType dX, TypeType dY) { return (dX > dY) ? dX : dY; } class Cents { private: int m_nCents; public: Cents(int nCents) : m_nCents(nCents) { } };
Cents cNickle(5); Cents cDime(10); Cents cBigger = getMaxgetMax(cNickle, cDime); /* C++ will create a template /* C++ will create a template instance for max() that looks instance for max() that looks like this: */like this: */ Cents max(Cents tX, Cents tY) { return (tX > tY) ? tX : tY; }
Function templates - Exercise
138
templatetemplate <classclass T> T Average(T *atArray, int nNumValues) { T tSum = 0; for (int nCount=0; nCount < nNumValues; nCount++) tSum += atArray[nCount]; tSum /= nNumValues; return tSum; }
Cents cArray[] = { Cents(5), Cents(10), Cents(15), Cents(14) }; cout << Average(Average(cArraycArray, , 44)) << endl;
Write Cents Class?Write Cents Class?
Templates classes
139
templatetemplate <typenametypename T> // template <class T>// template <class T> class Array { int m_nLength;
T *T *m_ptData; public: Array() { m_nLength = 0; m_ptData = 0; } Array(int nLength) {
m_ptData= new Tnew T[nLength]; m_nLength = nLength; } ~Array(){ delete[] m_ptData; }
T&T& operator[](int nIndex) { assert(nIndex >= 0 && nIndex < m_nLength); return m_ptData[nIndex]; } int GetLength(); }; templatetemplate <typenametypename T> int Array<T>::Array<T>::GetLengthGetLength() () { return m_nLength; }
int main() {
Array<Array<intint> > anArray(12);
Array<double>Array<double> adArray(12); for (int n = 0; n < 12; n++) { anArray[n] = n; adArray[n] = n+ 0.5; } return 0; }
Expression parameters
• A templatetemplate expressionexpression parameterparameter isis aa parameterparameter thatthat doesdoes notnot substitutesubstitute forfor aa typetype, but is instead replaced by a value
• An expression parameter can be any of the following: – A value that has an integral type or enumerationintegral type or enumeration
–– AA pointerpointer oror referencereference toto anan objectobject
–– AA pointerpointer oror referencereference toto aa functionfunction
–– AA pointerpointer oror referencereference toto aa classclass membermember functionfunction
140
Expression parameters
141
// // nSizenSize is the expression parameteris the expression parameter template template <typenametypename T, intint nSizenSize> class Buffer { // The expression parameter controls the size of the array// The expression parameter controls the size of the array
T m_atBuffer[nSizenSize]; public:
T*T* GetBuffer() { return m_atBuffer; }
T&T& operator[](int nIndex) {
return m_atBuffer[nIndex]; } }; // declare an integer buffer with room for // declare an integer buffer with room for 12 12 charschars
Buffer<Buffer<intint, , 1212> > cIntBuffer; // declare a char buffer with room for // declare a char buffer with room for 31 31 charschars Buffer<char, Buffer<char, 3131> > cCharBuffer;
Expression parameters
142
templatetemplate <classclass T, intint N> class mysequencemysequence { T memblock [N]; public: void setmember (int x, T value); }; templatetemplate <classclass T, intint N> void mysequencemysequence<T,N><T,N>::::setmember (int x, T value) { memblock[x]=value; }
Expression parameters
143
/*/* AnAn expressionexpression parameterparameter cancan bebe anyany ofof thethe followingfollowing:: AA valuevalue thatthat hashas anan integralintegral typetype …… */*/
template <typename T, floatfloat nSize>
class C{ };
template <typename T, double double nSize>
class C{ };
Template specialization
144
template <typename T> class Storage { T m_tValue; public: Storage(T tValue) { m_tValue = tValue; } ~Storage() { } void Print() { cout << m_tValue; } };
Storage<Storage<intint> > nValue(5); Storage<double> Storage<double> dValue(6.7); nValue.Print(); // ok// ok dValue.Print(); // ok// ok // but !!!// but !!! char *strString = new char[40]; cin >> strString; Storage<char*>Storage<char*> strValue(strString); delete delete strStringstrString;; // Print out our value// Print out our value // This will print garbage// This will print garbage strValue.Print(); // fix it using template specialization // fix it using template specialization
Template specialization - member functions
145
template <typename T> class Storage { T m_tValue; public: Storage(T tValue) { m_tValue = tValue; } ~Storage() {} void Print(){ cout << m_tValue;} }; Storage<char*char*>::Storage(char* tValue) { m_tValue = new char[strlen(tValue)+1]; strcpy(m_tValue, tValue); } Storage<char*char*>::~Storage() { delete[] m_tValue; }
specialization for char *specialization for char *
Class template specialization
146
template <typename T> class Storage8 { T m_tType[8]; public: void Set(int nIndex, const T & tType) { m_tType[nIndex] = tType; } const T& Get(int nIndex) { return m_tType[nIndex]; } };
// Define a Storage// Define a Storage8 8 for integersfor integers Storage8<int> cIntStorage; // Define a Storage// Define a Storage8 8 for for boolbool Storage8<bool> cBoolStorage;
it’sit’s possiblepossible toto compresscompress allall 88 boolsbools intointo aa singlesingle byte,byte, eliminatingeliminating thethe wastedwasted spacespace altogetheraltogether
Class template specialization
147
template <typename T> class Storage8 { T m_tType[8]; /* … *//* … */ }; // the following is a template class with no // the following is a template class with no templatedtemplated parametersparameters template <>template <> class StorageStorage88<<boolbool>> // we're specializing Storage// we're specializing Storage8 8 for for boolbool { unsigned char m_tType; public: void Set(int nIndex, bool tType) { unsigned char nMask = 1 << nIndex; if (tType) m_tType |= nMask; else m_tType &= ~nMask; } bool Get(int nIndex) { unsigned char nMask = 1 << nIndex; return m_tType & nMask; } };
Partial template specialization
148
// // nSizenSize is the expression parameteris the expression parameter template template <typenametypename T, intint nSizenSize> class Buffer { // The expression parameter controls the size of the array// The expression parameter controls the size of the array
T m_atBuffer[nSizenSize];
public:
T*T* GetBuffer() { return m_atBuffer; }
T&T& operator[](int nIndex) {
return m_atBuffer[nIndex]; } }; template template <typenametypename T, intint nSizenSize> void PrintBufferString(Buffer<T, Buffer<T, nSizenSize> &> &rcBuf) { std::cout << rcBuf.GetBuffer() << std::endl; }
Buffer<char, Buffer<char, 1010>> cChar10Buffer; strcpy(cChar10Buffer.GetBuffer(), "Ten");
PrintBufferString(cChar10Buffer); // Ten// Ten Buffer<Buffer<intint, , 1010> > cInt10Buffer; for (int n=0; n < 10; n++) cInt10Buffer[n] = n; // print the address of pointer// print the address of pointer PrintBufferStringPrintBufferString(cInt(cInt1010Buffer);Buffer);
ProblemProblem :: ensureensure thatthat onlyonly arraysarrays ofof typetype charchar cancan bebe passedpassed toto functionfunction
Partial template specialization
149
// Solution // Solution 11 void PrintBufferString(Buffer<char, Buffer<char, 1010> &> &rcBuf) { std::cout << rcBuf.GetBuffer() << std::endl; } // Problem of solution // Problem of solution 11 int main() { Buffer<char, Buffer<char, 1010>> cChar10Buffer; Buffer<char, Buffer<char, 1111> > cChar11Buffer; strcpy(cChar10Buffer.GetBuffer(), "Ten"); strcpy(cChar11Buffer.GetBuffer(), "Eleven"); PrintBufferString(cChar10Buffer); PrintBufferStringPrintBufferString(cChar(cChar1111Buffer)Buffer); // this will not compile// this will not compile return 0; }
Partial template specialization
150
// Solve problem of solution // Solve problem of solution 1 1 using using Partial template specializationPartial template specialization template<template<intint nSizenSize>> void PrintBufferString(Buffer<char, Buffer<char, nSizenSize> &> &rcBuf) { std::cout << rcBuf.GetBuffer() << std::endl; } int main() { Buffer<char, Buffer<char, 1010>> cChar10Buffer; Buffer<char, Buffer<char, 1111> > cChar11Buffer; strcpy(cChar10Buffer.GetBuffer(), "Ten"); strcpy(cChar11Buffer.GetBuffer(), "Eleven"); PrintBufferString(cChar10Buffer); // Ok// Ok PrintBufferStringPrintBufferString(cChar(cChar1111Buffer)Buffer); // Ok// Ok return 0; }
Exception handling • provides a mechanismmechanism to decoupledecouple handlinghandling ofof errorserrors or
other exceptional circumstances fromfrom thethe typicaltypical controlcontrol flowflow ofof youryour codecode
• Exceptions in C++ are implemented using three keywords that work in conjunction with each other: throwthrow, trytry, and catchcatch
•• throwthrow statementstatement is used to signalsignal that an exception or error case has occurred –– throw throw --11;; // throw a literal integer value// throw a literal integer value –– throw ENUM_INVALID_INDEX;throw ENUM_INVALID_INDEX; // throw an // throw an enumenum valuevalue –– throw “Error throw “Error 1010"; "; // throw a char* or string// throw a char* or string –– throw throw dXdX; ; // throw a double variable that was previously defined// throw a double variable that was previously defined –– throw throw MyErrorMyError("Fatal Error"); ("Fatal Error"); // Throw an object of class // Throw an object of class MyErrorMyError
151
Exception handling
152
trytry {{ // Statements that may throw exceptions you want to handle// Statements that may throw exceptions you want to handle throw throw --11;; }} // Any exceptions of type // Any exceptions of type intint thrown within the above try blockthrown within the above try block catch (catch (intint)) { { cerr << "We caught an exception of type int" << endl; }} // Any exceptions of type double thrown within the above try block// Any exceptions of type double thrown within the above try block catch (double)catch (double) {{ cerr << "We caught an exception of type double" << endl; }}
Exception handling - Example
153
double MySqrt(double dX) { if (dX < 0.0) throwthrow "Can not take "Can not take sqrtsqrt of negative number"of negative number"; return sqrt(dX); } int main() { double dX = -60; try { cout << "The sqrt of " << dX << " is " << MySqrtMySqrt((dXdX)) << endl; } catch (char* strException) // catch exceptions of type char*// catch exceptions of type char* { cerr << "Error: " << strException << endl; } }
Exception handling - stack unwinding
154
void Last() { cout << "Start Last" << endl; cout << "4 throwing int"; throw throw --11;; cout << "End Last" << endl; } void Third(){ cout << "Start Third" << endl; Last();Last(); cout << "End Third" << endl; } void Second() { cout << "Start Second" << endl; try { Third();Third(); } catch(double)catch(double) { cerr << “2 caught double“; } cout << "End Second" << endl; }
void First() { cout << "Start First" << endl; try { Second(); Second(); } catch (catch (intint)) { cerr << "1 caught int\n"; } catch (double) catch (double) { cerr << "1 caught double\n"; } cout << "End First" << endl; } int main() { cout << "Start main" << endl; try { First();First(); } catch (catch (intint)) { cerr << "main caught int\n"; } cout << "End main" << endl; }
Start main Start First Start Second Start Third Start Last 4 throwing int 1 caught int End First End main
Catch-all handlers
• C++ provides us with a mechanism to catch all types of exceptions
– it should be placed last in the catch block be placed last in the catch block chain
155
trytry { throw 5; // throw an // throw an intint exceptionexception } catch (double catch (double dXdX){){ cout << "caught an exception of type double: " << dX << endl; } catch (...) {catch (...) {// catch// catch--all handlerall handler cout << “caught an exception of an undetermined type" << endl; }
Exception handling - Example
156
double MySqrt(double dX) { if (dX < 0.0) throwthrow "Can not take "Can not take sqrtsqrt of negative number"of negative number"; return sqrt(dX); } int main() { double dX = -60; cout << "The sqrt of " << dX << " is " << MySqrtMySqrt((dXdX)) << endl; } /*main/*main terminatesterminates withwith anan unhandledunhandled exception,exception, thethe OSOS willwill generallygenerally notifynotify youyou thatthat anan unhandledunhandled exceptionexception errorerror hashas occurredoccurred */*/ int main(){ trytry { // program// program } catch(...)catch(...) { cerr << "Abnormal termination" << endl; } return 1; }
Exception specifiers • a mechanism that allows us to use a functionfunction declarationdeclaration
toto specifyspecify whetherwhether aa functionfunction maymay oror willwill notnot throwthrow exceptionsexceptions. This can be useful in determining whether a function call needs to be put inside a try block or not – an empty throw statement to denote that a function does not
throw any exceptions outside of itself • int DoSomething() throw()throw(); //// doesdoes notnot throwthrow exceptionsexceptions
– a specific throw statement to denote that a function may throw a particular type of exception
• int DoSomething() throw(double)throw(double); //// maymay throwthrow aa doubledouble
– a catch-all throw statement to denote that a function may throw an unspecified type of exception
• int DoSomething() throw(throw(......)); //// maymay throwthrow anythinganything
• exception specifies are rarely used in practice, are not well
supported by compilers
157
std::exception
• The C++ standard library comes with an exception class that is used by many of the other standard library classes.
158
class exception { public: /* … */ virtual const char* what() what() const throw(); }
Exception classes
159
#include <exception>#include <exception> class myExceptionmyException: public exception : public exception { virtual const char* what() const throw() { return "My exception happened"; } } ; int main () { try { /* ... */ throw throw myExceptionmyException();(); } catch (exception&exception& e) { cout << e.what() << endl; } return 0; }
Exception dangers and downsides
• What happens if WriteFile() fails
160
try { OpenFile(strFilename); WriteFileWriteFile((strFilenamestrFilename, , strDatastrData);); CloseFile(strFilename); } catch (FileExceptionFileException &&cExceptioncException) { cerr << "Failed to write to file: " << cException.what() << endl; }
Exception dangers and downsides
161
try { OpenFile(strFilename); WriteFileWriteFile((strFilenamestrFilename, , strDatastrData);); CloseFile(strFilename); } catch (FileExceptionFileException &&cExceptioncException) { CloseFileCloseFile((strFilenamestrFilename);); cerr << "Failed to write to file: " << cException.what() << endl; }
Exception handling - example
162
try { int * myarray= new int[1000]; } catch (bad_allocbad_alloc&&) { cout << "Error allocating memory." << endl; }