330 likes | 535 Views
Introduction to C++ Programming Module 2 Classes & Operator Overloading. Yaodong Bi, Ph.D. Department of Computing Sciences University of Scranton (717)941-6108 bi@cs.uofs.edu. Outline. Module 1 - An Overview of C++ and OO Programming Module 2 - Classes and Operator Overloading
E N D
Introduction to C++ ProgrammingModule 2Classes & Operator Overloading Yaodong Bi, Ph.D. Department of Computing Sciences University of Scranton (717)941-6108 bi@cs.uofs.edu
Outline • Module 1 - An Overview of C++ and OO Programming • Module 2 - Classes and Operator Overloading • Module 3 - Inheritance and Dynamic Binding • Module 4 - Function and Class Templates • Module 5 - C++ Stream I/O and Exception Handling
Review of Module 1 • C++ single-line comments: //comments • C++ stream I/O: cin >>, cout << • Declarations of variables: declare in the smallest scope • Inline functions: inline int square(int x) { return x*x;} • Reference parameters: int age = 30; int &rAge =age; • The const qualifier: const double PI = 3.1415; • The new and delete operators: int *ptr = new int(30); delete ptr; • Default arguments: void SetAge(int age =30); • Scope operator: cout << ::value;//global var. value • Function overloading: int square(int); double square(double);
Module 2: Classes & Operator Overloading • Class data members and member functions • Constructors & destructors • Copy constructors and copy assignments • Constant objects and constant member functions • The this pointer • Static members and member functions • Class objects as members of another class • Operator overloading • Friend functions and classes • Conversion operators
//Class Specification // Stack.h const int maxSize = 10; class CStack { public: // public interface CStack(int sz = maxSize); // constructor void Push (int item); int Pop(); bool IsEmpty() const; bool IsFull() const; ~CStack(); // Destructor private: // private implementation int* m_Array; int m_nTop; int m_nSize; }; //Class Implmentation // stack.cpp #include <iostream.h> #include "Stack.h" CStack:: // class scope operator CStack(int sz) { m_Array = new int[sz]; m_nSize = sz; m_nTop = -1; } CStack::~CStack() { delete [] m_Array; } Example 1: The Stack Class of Module 1
// stack.cpp - continued void CStack::Push(int item) { m_Array[++m_nTop] = item; } int CStack::Pop() { return m_Array[m_nTop--]; } bool CStack::IsEmpty() const { return (m_nTop < 0); } bool CStack::IsFull() const { return (m_nTop == m_nSize - 1); } // class.cpp #include <iostream.h> #include "Stack.h" void main() { CStack stack; for ( int i = 1; i < maxSize+3; i++) { if (!stack.IsFull()) stack.Push(i*100); } while (!stack.IsEmpty()) { cout << stack.Pop() << endl; } } Example 1: Stack - cont’d
Class members and member functions • Access Specifiers: • public: Any member or member function after public and before the next specifier is accessible to public. • void main() { CStack stack; ... stack.Push(item); ...} • Private: Any member or member function after private and before the next specifier is accessible only to the member functions of the class. void main() { CStack stack; ... stack.m_nSize = 10;// illegal - private member cout << stack.m_nSize;// illegal - private member ...} • Access specifiers may appear multiple times in a class definition although generally not recommended. class CStack { public: void Push(int); int Pop(); private: char* m_Array; int m_nSize; public: ~CStack(); …};
Class members and member functions • In-Class function definition: • Member functions may be defined in the class definition. • Defining member functions outside the class definition separates the interface from implementation • It should only be used for small, frequently used functions. • Put class definition into a .h file and member function definitions in a .cpp file. • When a member function is defined outside the class definition, the function name must be preceded by the class name int CStack::Push(int item); • Common Programming Error: Forgetting the semicolon at the end of the class definition class CStack { … };
// date.h class CDate { public: CDate(int dd=20, int mn=8, int yy=1998); void AddYear(int n); void AddMonth(int n); void AddDay(int n); void Print() const; private: int m_day; int m_month; int m_year; }; // date.cpp #include <iostream.h> #include "date.h" CDate::CDate(int dd, int mn, int yy) { m_day = dd; m_month = mn; m_year = yy; } void CDate::Print() const { cout << m_day << endl; cout << m_month << endl; cout << m_year << endl; } void CDate::AddYear(int n){/*...*/} void CDate::AddMonth(int n) {/*...*/} void CDate::AddDay(int n) {/*...*/} Example 2: A Date Class
Class Constructors and Destructors • Constructors • Initialize the objects of the class type -- Don’t initialize members in class definition. • Constructors are member functions with the same name of the class. Constructors cannot specify return types nor return values. • Constructors obey the same overloading rules as do other functions class CStack { CStack(int size); // constructor with one argument Cstack(); …} ; // constructor without arguments • Constructor may take default argument. class CStack { CStack(int size = 10); …} • Default constructor: a constructor that can be called without supplying an argument class CStack { CStack(int size = 10); …} // default constructor class CStack { CStack(); …} // default constructor // don’t use both in the same class -- ambiguous
Class Constructors and Destructors • When no user-defined constructor is found, the compiler would generate one as needed. Classes with const’s or references cannot be constructed by a compiler-generated constructor. • Accessing data members of a class in its constructors may cause error because the members may not have been properly initialized • Destructors • Clean up and release resources • They are called when an object of the type goes out of scope, or when an object is deleted. class CStack { ~Cstack(); // Destructor …} ; • Common use is to release memory acquired in constructors. CStack::CStack(int size) { … m_Array = new char[size]; ..} CStack::~CStack() { delete [] m_Array;}
Class Constructors and Destructors • When destructor needs defined? • In the date class, the constructor does not acquire any system resources including memory. Thus, when a date object goes out of scope, the memory occupied by the data members is collected automatically by the system. • For the date class , no destructor needs to be explicitly defined • In contrast, the stack class’s constructor acquires memory from the free store. When a stack object goes out of scope, the memory occupied by the data members are collected. For m_Array, the pointer itself is removed, but the memory of the string pointed by m_Array will not collected automatically. Thus, the memory acquired in the constructor must be freed explicitly before the stack object is destroyed. • Destructors should be defined, when system resources that are generally acquired in the constructors must be manually released. • For the stack class, a destructor must be defined to release the memory acquired in the constructor.
Const objects and const member functions • Constant objects; • Use const to specify that an object is not modifiable. • Ex: const CDate date; date.AddDay(7);// illegal • const member functions. • A const function is declared with const and it does not modify the object. • Ex: CStack::IsEmpty() const and CDate::Print() const • Only const member functions can acess a const object. • Ex: const CDate date; date.AddDay(7);// illegal date.Print(); // okay • const must be used in both the declaration and definition of the member function • A const member function can be overloaded with a non-const version. Good Design??
Copy constructor and copy assignment • Memberwise copy • When an object is assigned to another object of the same type, by default, memberwise copy is performed. • Ex: In the date class: date1 = date2; implies date1.m_day = date2.m_day; date1.m_month = date2.m_month; date1.m_year = date2.m_year; • Memberwise copy may not be proper for some classes, such as the stack class. • By memberwise copy, stack1 = stack2; would perform stack1.m_Array = stack2.m_Array;// only the pointer is copied stack1.m_nSize = stack2.m_nSize; stack1.m_nTop = stack2.m_nTop; • The problem is that both stacks now refer to the same array • Two cases: • Copy initialization, ex., CStack stack1 = stack2; and void foo(CStack); • Copy assignment, ex., CStack stack1; stack1 = stack2;
Copy constructor and copy assignment • Copy constructors • The copy constructor is called when an object is initialized with anther object of the same class. Ex: CStack stack1 = stack2; // stack1 is defined and initialized void foo(CStack stack);// stack will initialized with the passed. • Copy constructor definition: ClassName(const ClassName&); class CStack { ... CStack(const CStack&); // copy constructor declaration … }; CStack::CStack(const CStack& stack) // copy constructor definition { m_nSize = stack.m_nSize; m_nTop = stack.m_nTop; m_Array = new int[m_nSize]; for (int i=0; i<=m_nTop; i++) m_Array[i] = stack.m_Array[i]; }
Copy constructor and copy assignment • Copy Assignments • The copy assignment is called when an object is assigned with anther object of the same class. Ex: CStack stack1; ... stack1 = stack2; // stack1 is assigned with stack2 • Copy assignment definition: • General form: ClassName& operator=(const ClassName&); • The stack class class CStack { ... CStack& operator=(const CStack&); … }; // copy assignment declaration CStack& CStack::operator=(const CStack& stack) { // copy assignment definition delete [] m_Array; m_nSize = stack.m_nSize; m_nTop = stack.m_top; m_Array = new int[m_nSize]; for (int i=0; i<=m_nTop; i++) m_Array[i] = stack.m_Array[i]; return ???? }
The this pointer • In each member function, there is a pointer to the object for which the function was invoked -- this. • In a non-const member function the type of thisis X* const; in a const member function its type is const X* const. • It can be used explicitly to reference the members of the object. bool CStack::IsEmpty() { return this->m_nTop < 0;} • The stack class -- what if it is a self-assignment CStack& CStack::operator=(const CStack& stack) { if (&stack != this) { delete [] m_Array; m_nSize = stack.m_nSize; m_nTop = stack.m_nTop; m_Array = new int[m_nSize]; for (int i=0; i<=m_nTop; i++) m_Array[i] = stack.m_Array[i]; } return *this; }
// date.h class CDate { public: static void SetDefault(int, int, int); CDate(int dd=0, int mn=0, int yy=0); void Print() const; ~CDate(); private: int m_day; int m_month; int m_year; static CDate default_date; }; // data.cpp #include <iostream.h> #include "date.h" void CDate::SetDefault(int dd, int mn, int yy) { default_date = CDate(dd, mn, yy); } CDate::CDate(int dd, int mn, int yy) { m_day = dd? dd : default_date.m_day; m_month = mn ? mn : default_date.m_month; m_year = yy ? yy : default_date.m_year; } Example 3: Modified Date Class
// data.cpp -- cont’d CDate::~CDate() {} void CDate::Print() const { cout << m_day << endl; cout << m_month << endl; cout << m_year << endl; } // The static member is initialized here CDate CDate::default_date(19, 12, 1961); // examp3.cpp // main program #include <iostream.h> #include "date.h" void main() { CDate initial; CDate userSpecified(1, 1, 2000); CDate::SetDefault(20, 8, 1998); CDate newDefault; cout << "Default date:\n"; initial.Print(); cout << "User Specified Date:\n"; userSpecified.Print(); cout << "New Default Date:\n"; newDefault.Print(); } Example 3: Date Class -- cont’d
Static Members and Member Functions • Static member: a variable that is part of a class, but is not part of an object of the class. Only one copy for all the objects of the class. static CDate default_date; • Static member function: a function that needs to access to members of the class, yet doesn’t need to be invoked for individual object. static void SetDefault(int, int, int); • Static members must be initialized at file scope. CDate CDate::default_date(19, 12, 1961); // in date.cpp, not in main() • Public static members (and functions) can be accessed via an object of the class or qualified with the class name. class CDate {… public: static CDate default_date; …} ptr->default_date = userSpecified; // okay CDate::default_date = userSpecified; // okay- preferred initial.SetDefault(20, 8, 1998); // okay CDate* ptr;… ptr->SetDefault(20, 8, 1998); // okay • Static members exist even when no objects of the class exist.
Class Objects as Members of Another Class class CDate; class CPerson { public: CPerson(const string& name, CDate date); ~CPerson(); string GetName() const; int GetAge() const; void SetName(string); void SetDate(int, int, int); virtual void Print() const; private: string m_name; CDate DOB; }; • Class Date is embedded in CPerson. • Class string is in CPerson also. • Constructor with member initialization. CPerson::CPerson(string nm, CDate date) :m_name(nm), DOB(date) { } // m_name and DOB are initialized with their // copy constructors • Constructor with no initialization CPerson::CPerson(string nm, CDate date) {m_name=nm; DOB= date; } // m_name and DOB are first initialized with // their default constructors( user defined or // compiler generated) , then m_name and // DOB are set with their copy assignments or // memberwise copy.. • Member initialization is preferred. When a class has const’s or references, they can only be member-initialized
// string.h class CString { public: CString(char *str = 0); CString(const CString& str); CString& operator=(const CString& str); CString operator+(const CString &str); int GetSize() const; void Print() const; ~CString() {delete [] m_str;} private: char * m_str; int m_size; }; // string.cpp #include <cstring> #include <iostream> #include "string.h" using namespace std; CString::CString(char *str) { if (str != 0) { m_size = strlen(str); m_str = new char[m_size+1]; for (int i = 0; i < m_size+1; i++) m_str[i] = str[i]; } else { m_str = 0; m_size = 0; } } Example 4: A String Class
// string.cpp -- cont’d CString::CString(const CString& str) { m_size = str.GetSize(); m_str = new char[m_size+1]; for (int i = 0; i < m_size+1; i++) m_str[i] = str.m_str[i]; } CString& CString::operator= (const CString& str) { if (&str != this ) { m_size = str.m_size; delete [] m_str; m_str = new char[m_size+1]; for (int i = 0; i < m_size+1; i++) m_str[i] = str.m_str[i]; } return *this; } // string.cpp -- cont’d CString CString::operator+(const CString &str) { CString NewStr; NewStr.m_size = str.m_size + m_size; NewStr.m_str = new char[NewStr.m_size+1]; strcpy(NewStr.m_str, m_str); strcat(NewStr.m_str, str.m_str); return NewStr; } int CString::GetSize() const { return m_size; } void CString::Print() const { cout << m_str << end; } Example 4: String -- cont’d
// main.cpp #include <iostream.h> #include "string.h" void main() { CString str1("Hello, "); str1.Print(); cout << endl; CString str2("world!\n"); str2.Print(); CString str3 = str1+str2; str3.Print(); str3 = str1+"attached\n"; str3.Print(); } Overload operator + CString& CString::operator+(const CString &str) { ... } Overload operator = CString&CString::operator= (const CString& str) { ... } CString str3 = str1+str2 in main() operator+() is called first for str1+str2, the result is held in a temporary location. Then, the copy constructor is called to initialize str3. str3 = str1+”attached\n” in main() The default constructor is called to convert “attached\n” to string tmp, then, operator+() is called for str1+tmp. Lastly, the copy constructor is called to initialize str3, and tmp is deleted. Example 4: String -- cont’d
Operator Overloading • Operator Functions as Class Members • 40 operators can be overloaded. See Fig. 18.1 in the text. • The precedence of an operator cannot be overloaded. • The # of operands of an operator cannot be changed. • Default arguments cannot be used to overload an operator. • Treat “operator+” together as a function name str3 = str1+"attached\n";is equivalent to str3 = str1.operatpr+(”attached\n”); • Overload operators to perform the same or similar functions on class objects as the operators perform on objects of built-in types • Nonmember Operator Functions ostream& operator<<(ostream& output, const CString& string) { cout << string.m_str << endl; // is this okay???? return output; } CString operator+(const char* cstr, const CString& str) { return CString(cstr) + str; } CString str1, str2; ... ;str2 = “string” + str1;…; cout <<str1 <<str2;
Friend Functions and Classes • Friend function: a nonmember function that has been given by the class the right to access private (and protected) members of the class. class CString{friend ostream& operator<<(ostream&, const CString&); ...};// declare a friend ostream& operator<<(ostream& output, const CString& string)// define the friend { cout << string.m_str << endl;// if not a friend, this would be illegal return output; } CString string; .... cout << string;// use the friend function • Friends can be declared anywhere in the class definition. • Friend class: a class that has been given by the class the right to access private (and protected) members of the class class CString { friend class CStringStack;// Declare CStringStack as a friend .... }; class CStringStack { ... void Print(); ...// print all the strings in stack private: CString* m_Array;// an array of CString; .... };
// CStringStack.cpp ...... void CStringStack::Print() { for (int i=0; i<=m_nTop; i++) { // Access private member m_str of class CString cout << m_Array[i].m_str << endl; } } // main.cpp void main() { ...... CStringStack stack; stack.Push(str1); stack.Push(str2); ..... stack.Print();// print all the strings in the stack ... } Friend Classes -- cont’d
Conversion Operators • A class can define how it can be converted to a prefined or a user defined data type. • Syntax -- convert X to T operator T() const; // declaration X::operator T() const { .... } //definition // no returned values and no arguments • Examples class CString {public: ...; operator char*() const; // declaration operator UserDefinedTypes() const; // declaration ...} CString::operator char*() const { return m_str; } //definition CString::operator UserDefinedTypes() const { ... }// definition CString string(“Hello”); char* ptr = string;// conversion operator char*() called here cout << ptr; ptr = 0;
Advice • Use member initialization to initialize const and reference members. • Pass big objects as const X& to functions. • Declare as const all member functions that do not change the object. • Don’t forget the semicolon at the end of a class definition • Declare classes in the order of friends, public, protected, and private. • Use member initialization to initialize embedded objects • Make a function a member only if it accesses the representation of the class • If a constructor acquires resources, define a destructor to release them. • If a class has a pointer member, it is likely you need copy operations (constructor and assignment). • Check for self-assignment in copy assignments • Don’t return a private member (or its address) to a non-const reference (or a pointer).
Summary of Module 2 • Class data members and member functions • Constructors & destructors • Copy constructors and copy assignments • Constant objects and constant member functions • The this pointer • Static members and member functions • Class objects as members of another class • Operator overloading • Friend functions and classes • Conversion operators
Programming Assignments • Design a time class. • Similar to the date class, it contains hour, minute, and second. • Use a static member for class-wide default time. • Code a main() to test it. • Improve Example 3: Modified Date Class • Overload I/O operators << and >> as nonmember functions ostream& operator<<(ostream& output, const CDate& date); istream& operator>>(istream& input, CDate& date); *see section 18.5 (pp. 685-687) of the text for an example • Modify the main() function to test your operators. • Design an integer array class -Incremental development • Start with a simple array class with basic operations. • Add features like overloading operators [], ==, !=, <<, >> etc. and range checking. • Having trouble? *See 18.8 (pp. 689-698) for a complete example. *H. Deitel and P. Deitel, C How to Program, 2nd Ed., Prentice Hall, 1994.
Additional Programming Assignments 1. For the CString class, overload the equality operator "==" as a member function of CString. It returns true when the two CString objects (the two operands) are the same(same length and same string content), return false otherwise. When the two operands (the two CString objects) are the same object (hint: the this pointer), return true. 2. Modify the conversion operator CString::operator char*() const. Instead of returning the address of the existing string of characters, it dynamically allocates a new character array and copies the existing string into the new string and then returns the address of the new string. Thus, the char* pointer would not point to the private member (m_str) of CString 3. Add another static member to CDate. The new static member keeps track of the number of CDate objects that exist in the system. Hint: Initialize the counter to zero. Increment it by one in every constructor and decrement it by one in the destructor. 4. In the section of friend classes, we used a stack (CStringStack) of CString objects as an example and declared CStringStack as a friend class of CString. complete the design and coding of CStringStack by coding a default construnctor, a destructor, the Push, Pop, IsEmpty, and IsFull functions. When the copy constructor/ assignmeng of CString would be called? If the CString class did not define the copy constructor/assignment, would your program run correctly? 5. Continue assignment 4. Design a copy constructor and a copy assignment for CStringStack. Make sure check for self-assignment in the copy assignment operation.