270 likes | 358 Views
Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică. Object Oriented Programming. Lect. Dr. Daniel POP. Course #3 Agenda. Classes in C++ Declaration and implementation Accessing class members. Access control modifiers Constructors
E N D
Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică Object Oriented Programming Lect. Dr. Daniel POP
Course #3 Agenda • Classes in C++ • Declaration and implementation • Accessing class members. Access control modifiers • Constructors • Destructor • Self-reference • Modifiers: static, const, mutable, friend Object-Oriented Programming
Classes in C++ • Classes are defined in C++ using struct or class keywords. • Syntax: • Example: class X { // member variables // member functions }; struct X { // member variables // member functions }; struct Date{ int day, month, year; // Date initialization void init(int day, int month, int year); }; X – generic name for a class Remark: In case of using struct, by default, all members are public. For class, they are private by default. The programmer is free to choose which keyword he/she prefers, with a recommendation to use struct for data types that doesn’t provide behaviour. Object-Oriented Programming
Scopes • DEFINITION [Scope] A scope is a specific part of the source code. • Example: any block (delimited by { and }) defines a scope; each function defines a scope. • Each class defines a new scope. The name of the scope is the same as class name. • A name is defined in the globalscope if it is defined outside any function, class or namespace. It is visible from the point of declaration up to the end of the file. • Every declaration (variable of functions) introduces a name in a scope => this name is visible only in that scope and hides names declared in enclosing scopes or at global scope. • The scope resolution operator (::) allows to access an identifier (name) defined in another namespace than the current one. • Syntax: • Example: • What happens here? int x; // global x void f() { int x; // local x hides the global x x = 1; // local x = 1 ::x = 2; // global x = 2 } int* px = &x; // address of global x int x = 0; void f() { int y =x; int x = 1; y = x; } ScopeName :: Identifier [Stroustrup, 1997 – Section 4.9.4] Object-Oriented Programming
Implementing member functions • A member function is handled as any other ordinary C function. • A member function is a name introduced in the scope defined by its parent class. Therefore, to access this name outside its scope of definition, we need to fully qualify the name using the scope resolution operator and the class name (namespace). • Example: This indicates that init is the identifier from Date class/namespace and not defined at global scope! void Date::init(int _day, int _month, int _year) { day = _day; month = _month; year = _year; } Object-Oriented Programming
Accessing class members • Access to members of a class – the same syntax as for struct in C, i.e. using ‘.’ or ‘->’ operators. • One need to have an object through which members are “incarnated” and accessed (of course, there are some exceptions… but we’re saving them for later) • Syntax: Object.member; PObject->member; • Example: void f() { Date today; Date *pdate = &today; today.init(7, 3, 2007); // March 7th, 2007 printf(“Todayis %d/%d/%d.”, pdate->day, pdate->month, today.year); } Object-Oriented Programming
Accessing class members • New operators in C++: ‘.*’ and ‘->*’ // pointer to int data member of Date typedef int Date::*PM; // pointer to member function of Date taking 3 arguments of type int typedef void (Date::*PMF)(int, int, int); void f() { Date today; Date *pdate = &today; PM pm = &Date::day; PMF pmf = &Date::init; (today.*pmf)(7, 3, 2007); // today.init(7, 3, 2007); today.*pm = 8; // today.day = 8 pm = &Date::month; today.*pm = 8; // today.month = 8 (pdate->*pmf) (7, 3, 2007); // pdate->init(7, 3, 2007); pdate->*pm = 8; // pdate->month = 8 } Object-Oriented Programming
Access Control • To implement the data hiding principle in C++, various level of access are defined for class members (data or functions): • private – accessible only to member functions and friends; • protected – accessible to member functions and friends + member functions and friends of derived classes; • public – accessible from everywhere. • Public members define the public interface of one class. • Example: class MyClass { int x; // private, by default public: int y, z; // public int f(); // public member function private: int w; // private data protected: int f(int, int); // protected member function }; void main(){ MyClassobj; obj.x = 10;// ERROR: Cannot access private members obj.f(); // OK! obj.f(1,1); // ERROR:Cannot access protected members } Object-Oriented Programming
Constructors (I) • Rationale: to prevent errors caused by non-initialized objects • Example: Date objects, a Stack with dangling pointer to its top element etc. • DEFINITION [Constructor] A member function of a class whose role is to initialize the class instances (objects). • Object creation process: • Memory allocation • Find an appropriate constructor • Call the constructor to initialize the object’s state after the data members have been previously constructed/initialized by calling their constructors • Q: Which member functions are the constructors? • A: The constructor name = class name • Characteristics of constructors: • The name = Class name • They have NO return value (Remark: void is a return value! => don’t use it) • No pointers can be assigned to constructors (PMF pmf = &Date::Date; is illegal) • Except for the above constraints, constructors are handled as any other member function of a class (for example, they may have 0, 1 or more arguments, can call other member functions etc.) Object-Oriented Programming
Constructors (II) class Date{ int _day, _month, _year; void init(int day, int month, int year); public: // Constructors! Date(int day = 0, int month=0, int year=0); }; void f() { Date d3; // CORRECT: Constructor found } • Example: class Date{ int _day, _month, _year; void init(int day, int month, int year); public: // Constructors Date(int day, int month, int year); Date(int day, int month); Date(int day); }; void f(){ Date d0 = Date(6, 10, 2003); // correct Date d1(1, 1); // correct Date d2(15); // correct Date d3; // ERROR: No appropriate constructor is found Date* pd = new Date(7, 10, 2003); // correct } #include “Date.h” Date::Date(int day, int month, int year) { _day = day ? day : today.day; _month = month ? month : today.month; _year = year ? year : today.year; } Object-Oriented Programming
Constructors (III). Default Constructor • Constructor without arguments • Prototype: X(); • If a class has no constructors then the compiler generates a default constructor for that class that implicitly calls the default constructors for class’ members (of class type) and base classes. • Remark: If a class has const or reference members then the default is not generated automatically because consts and references must be initialized. • Examples: class Date{ public: // default constructor Date(int day=0, int month=0, int year=0); }; class String { public: String(); // default constructor }; class Student { Date birthday; String name; // automaticlly generated default constructor that calls Date and String // defaults constructors }; class Date { public: // no default constructor is generated Date(int day); }; class Test { const int a; int& r; // no default constructor is generated }; Object-Oriented Programming
Constructors (IV). Copy Constructor • Constructor with one argument of type reference to its own class • Prototype: X(const X&); • const – indicates that the source object is not modified • It is called: • Declaration of an object like X obj = obj_source; • Passing an object as an argument to a function call func(X); WHY? • When a temporary object is created during expression evaluation • If it is not defined for one class, then the compiler automatically generates one that copies bit-by-bit the content of source object to destination object. • To prevent copying of objects, the copy constructor can be declared private (no implementation is needed). • To ‘deep’ copy complex objects, the copy-constructor is mandatory! (Examples in lab) • Examples: void g(Date d) { d.setDay(15); } void f() { Date d(7, 3, 2007); // user-defined constructor Date d1; // default constructor Date d2 = d; // copy constructor d2 = d; // NO copy constructor g(d); // copy constructor is called } class Date{ public: Date(int day=0, int month=0, int year=0);// default constructor Date(const Date& ref); // user-defined copy constructor void setDay(int day); }; Object-Oriented Programming
Constructors (V). Type Conversion Constructors • Scope: convert from one data type to a class (built-in type | user-defined-type class) • Prototype: X(Datatype); • Called in • declarations like X x = value; where value has type = Datatype (1) • function calls to cast the actual argument to required type (2) class Double{ double val; public: Double(double v) { value = v; } } ; vod f(){ // three equivalent declarations Double obiect = 2.5; Double obiect = Double(2.5); Double obiect(2.5); } class Date{ public: // … Date(char* str) ; // type-conversion constructor }; void g(Date d) { d.setDay(7); } void f() { Date d = “6/3/2007”; // (1) g(d); g(“This is tricky ;-)”); // (2) } Object-Oriented Programming
Destructor • Rationale: to prevent errors caused by un-released objects (unreleased resources). • Example: Stack without freeing the memory used, File without closing the handle etc. • DEFINITION [Destructor] A member function of a class whose role is to release the class instances (objects) from memory. • Object destruction process: • Call the destructor function • Call destructors of data member • Free the memory • Q: Which member function is the destructor? • A: The destructor name = ~class_name (i.e. ~X) • Characteristics of destructor: • Prototype: X::~X(); • They have NO return value (Remark: void is a return value) and they take no arguments. • No pointers can be assigned to destructor (PMF pmf = &Date::~Date; is illegal) • Except for the above constraints, destructor is handled as any other member function of a class. Object-Oriented Programming
Destructor (II) • A class can have at most one destructor. • The compiler generates an empty default destructor. • Example: class String { char* str; public: // Constructors String(char* psz=NULL); ~String(); }; void f(){ String s1; String s2=“We like C++;)”; String s3=s2; String* s4 = new String(“Do we?”); delete s4; // destructor is called } // destructors for s3, s2, s1 are called in this order String::String(char* psz) { if(psz==NULL) str=NULL; else { str = new char [strlen(psz)+1]; strcpy(str, psz); } } String::~String() { if(str!=NULL) delete [] str; } Try this & Explain the result! Object-Oriented Programming
Constructors & Destructor • Both, constructor and destructor can be explicitly called. NOT RECOMMENDED TO DO THIS! • Example void f(){ String s1; s1.String::String(“Explicit call”); s1.~String(); } • RECOMMENDATION: If a class has a pointer member, then it (most likely) needs: • a copy constructor, • a destructor and • an assignment operator (see operator overloading). Object-Oriented Programming
Self-Reference • Example: void f() { Date d; d.init(25,12,2007).getMonth(); } Date& Date::init(int day, int month, int year) { this->_day = day; // _day = day _month = month; // implicit use of this this->year = year; // year – makes possible // to use the same name // for members and args this++; // ILLEGAL: this is a const pointer return *this; } • => The prototype of init function should be: Date& init(int, int, int); • Each non-static member function knows what object it was invoked for and can explicitly refers to it using this keyword. • this is a pointer to the object for which the function was called • For non-const function its type is: X* const this; • For const function its type is: const X* const this; • Similar in Java (this), Smalltalk (self) or Simula (THIS). Object-Oriented Programming
Modifiers: static (I) • DEFINITON [static data member] A variable that is part of a class, yet is not part of an object of that class is called a static member. • There is exactly one copy of a static variable ‘shared’ by all objects of that class. • Static data member = ‘global variable’ defined and accessible only in the scope of that class. • DEFINITION [static member function] A function that needs access to members of a class, yet doesn’t need to be invoked for a particular object, is called a static member function. • Static member function = ‘global function’ defined and accessible only in the scope of that class. • Declaration of static members: • Accessing static members: static MemberDefinition; X::memberName; obj.memberName; • In order to clearly differentiate between static and non-static members + enhance code readability, the first (bold) syntax is recommended to be used. Object-Oriented Programming
Memory representation of static data. Modifiers: static (II) class Date{ int day, month, year; static Date today; // declare the static data member public: // … static void initToday(); }; Date Date::today; // create the static data member void Date::initToday() { time_t t; time(&t); tm* now = localtime(&t); day = now->tm_mday; // ERROR: whose day is this day? today.day = now->tm_mday; today.month = 1 + now->tm_mon; today.year = 1900 + now->tm_year; } int main(int, char*[]) { Date::initToday(); return 0; } • REMARKS: • static data members have to be defined/initialized somewhere (in the class definition they are only declared!) • static member functions DO NOT receive this pointer • => the access to non-static members (data/function) is not possible from within static functions • creation, initialization and access to static members are independent of objects existence. • Example: default date (today) in Date class Object-Oriented Programming
Modifiers: const (I) • Data members • Cannot be modified! • Useful to declare constants at class scope • Constant data members must be initialized in the initialization list of each constructor of the class • Nota: initialization list – to be addressed in detail in coming classes • Usually, it makes sense to have constant static members because constant values are usually shared between all the instances of a class • Syntax: class X { const int ci; static const int MAX_VIEWS; // declaration public: X () : ci(17) { // initialize const members inside an initialization list } }; // initialization of static const members const int X::MAX_VIEWS = 256; const data_type name; Object-Oriented Programming
Modifiers: const (II) • Member functions: • Cannot modify the state of the object (i.e. data members) • Enhances the code’s clarity • Prevents accidental updates • When the function is implemented outside its class, const suffix is required (see getMonth() in example) • Syntax: class Date{ int day, month, year; static Date today; // declare the static data member public: //const, inline member function int getDay() const { day = 0 ; // ERROR: we’re in const function return day; } int getMonth() const; }; int Date::getMonth() const { return month; } int main(int, char*[]) { Date d; cout << d.getDay() << d.getMonth() << d.getYear(); return 0; } ftype fname(arg_list) const; Object-Oriented Programming
Modifiers: mutable • Applies only to data members • Can always be modified, even from within const functions! • Useful for members that need to be changed in const functions and don’t represent the actual internal state of the object • Syntax: class Date{ int day, month, year; mutable string cache; // always changeable! mutable boolean validCache; // always changeable! public: // const member function string toString() const; }; string Date::toString() const { if(!validCache) { cache = compute_new_string_representation(); validCache = true; } return cache; } mutable data_type name; Object-Oriented Programming
Friends (I) class Matrix { /* declarations */ } ; class Vector{/* declarations */ }; Define a function that: Vector x Matrix Vector • Rationale: functions need access to private members of one class • Solution: make those functions members of the class • What happens if one function need to have access to private members of 2 or more classes? It can be member of only one class :-( • Friends – help us to do this :-) Object-Oriented Programming
Friends (II) • DEFINITION [Friend functions] Friend functions are functions that are not members of one class, yet they have access to private members (data + functions) declared in that class. • DEFINITION [Friend class] If class X is friend class of class Y, then all member functions of class X are friend functions (i.e. have access to private members) of class Y. • Friends can be either functions that are member of another class, or global functions. • Syntax: friend ftype fname(arg_list); friend class X; • It’s not relevant the access control modifier (private, public, protected) used to declare a friend function/class, because friend functions are not member of the class for which they are friend. • REMARK: One of the key principle OO is data hiding (encapsulation), but sometimes is to restrictive and needs to be “broken” Not recommendable to use friends; use only if it’s impossible to solve the problem otherwise Object-Oriented Programming
Friends (III) • Example: class Matrix; // Forward declaration class Vector{ float v[4]; public: Vector(float v0=0, float v1=0, float v2=0, float v3=0); friend Vector multiply(const Matrix&, const Vector&); }; class Matrix{ Vector rows[4]; friend Vector multiply(const Matrix&, const Vector&); }; Vector multiply(const Matrix& m, const Vector& v){ // HOMEWORK } void main(){ Matrix m; Vector v(1.0, 2.5, 5.0, 6.0); v = multiply(m, v);} • More examples: see operator overloading • Finding friends: declared before or identifiable by argument type • Friendship is NOT reflexive nor transitive • Friendship is not inherited • Rights and restrictions vs. various function types => Object-Oriented Programming
Member or Friend? • Some operations can be performed only by members: constructor, destructor, virtual functions • A function that has to access the members of a class should be a member unless there’s a specific reason for it not to be a member. • Prefer member functions over friends because of clearer syntax. • Member functions must be invoked for objects of their class only; no user defined conversions are applied. • For example, if transpose function transposes its calling object instead of creating a new transposed matrix then it should be a member of class. • Example: (see also operator overloading for more examples) class X { public: X(int); void m1(); friend int f1(X&); friend int f2(const X&); friend int f3(X); }; void f() { 7.m1(); f1(7); f2(7); f3(7); } void f() { 7.m1(); // ex1; X(7).m1() is not tried! f1(7); // ex2; f1(X(7)); is not tried because no implicit conversion is used for non-const references f2(7); // ok: f3(X(7)); f3(7); // ok: f3(X(7)); } Object-Oriented Programming
Further Reading • [Pop, 2003] Daniel Pop – C++ Programming Language Lecture Notes • [Stroustrup, 1997] Bjarne Stroustrup – The C++ Programming Language 3rd Edition, Addison Wesley, 1997 [Chapter 10] Object-Oriented Programming