Embedded Systems Programming C Classes Contents Introduction User
Embedded Systems Programming C++ Classes
Contents • • Introduction User defined data types Classes and encapsulation Implementation/interface Classes and objects Creating/initializing objects – constructors Destroying objects – destructors Other features of classes
Introduction • Classes are the core of C++’s support for object oriented programming • They allow the programmer to create abstract data types in a similar way that struct does in C – However the C++ class also supports encapsulation whereas struct does not – Encapsulation allows the programmer to hide details about the implementation • First we will review the struct construct
User defined data types • C (and C++) allow user defined data types to be created using the struct construct • For example, we could create a Date data type to represent dates struct Date { int d; int m; int y; };
• We could create variables (objects) of the Date data type as well as writing functions that operate on these objects extern void set_date(Date*, int, int); extern void print_date(const Date*); void main() { Date* date = new Date; set_date(date, 28, 8, 2003) print_date(date); }
• In C++, we can include the functions as part of the struct declaration so they become member functions – This means we don’t have to pass the objects as arguments into the functions struct Date { int d; int m; int y; void set(int, int); void print(); };
• The member functions are defined using the : : operator as follows : void Date: : set(int dd, int mm, int yy) { d=dd; m=mm; y=yy; } void Date: : print() { printf(“Day, month, year = %d %d %d ”, d, m, y); }
• Finally Date objects can be created and member functions called as follows : – Note that member functions are called through the object (variable) name void main() { Date today; today. set(12, 8, 2003) today. print(); }
Classes and encapsulation • Classes in C++ are similar and have similar syntax and uses as structures in C with one key difference – They support the idea of encapsulation – The implementation of the class is hidden from the interface to the class – This is achieved through the use of public and private member access specifiers
• We can define a Date class as follows : class Date { private: int d; int m; int y; public: void set(int, int); void print(); void advance(); };
• An additional member function advance() has been added which advances the date by one day • Apart from that, this has the same functionality as the Date structure • The key difference is the separation of the class into private and public • This separation underpins the ideas behind encapsulation (or implementation/interface)
Key point • Only public member functions of Date can access private members of the class – Enforced by rules of the language – If we try to access a private class member from outside a public member function, a compilation error is triggered
• Implementation of Date: : set() is as follows void Date: : set(int dd, int mm, int yy) { d=dd; m=mm; y=yy; } • Accesses Date: : d, Date: : m, Date: : y which is OK as it’s a public member function
• The following is illegal code int get_year(Date date) { return date. y; // Error! } int main() { Date today; today. d=28; today. m=8; today. y=1998; } // Error! • This is an attempt to access a private class member from outside a public member function
• In order to access the private class members Date: : d, Date: : m, Date: : y, we have to provide public member access functions class Date { private: int d; int m; int y; }; public: void set(int, int); void print(); void advance(); int day() {return d; } // access function int month() {return m; } // access function int year() {return y; } // access function
• You may wonder (quite rightly!) whether this is an undue computational overhead – In order to access a private class member, we have to go through an additional function call • However, access restriction of the classes private members is key to encapsulation – Implementation/interface
Implementation/interface • We can justify the sub-division of a class into private and public by considering the idea of the implementation of the class and the interface to the class • The implementation of the class is represented by the private class members and public member function bodies (which use the private members) • The interface to the class is represented by the public member functions headers
Date private int d int m int y Implementation public void set() void print() void advance() int date() int month() int year() Interface
Changing the implementation of Date • Suppose we decide to change the implementation of Date and represent the date internally in the Julien format – This represents the number of days since 1/1/1900 from which the current day, month and year can be computed algorithmically
class Date { private: long julien; }; // Number of days since 1/1/1900 public: void set(int, int); void print(); void advance(); int day() {return d; } int month() {return m; } int year() {return y; } // access function
• The key point is that public member function body implementations need to change to reflect the changed private members • However, the public member function headers remain the same • These represent the interface to the Date class as seen by application programs using the class
Date : : day() { // complicated algorithm } Date : : month() { // complicated algorithm } Date : : year() { // Simple algorithm (taking into account leap years) } Date: : advance() { julien++; }
Key point • Because of encapsulation, applications using the Date class will not change as they only interact with the class through public member function calls • This leads to more robust extendible systems
• For example, some external function weekend() will still be correct even after the changes to Date – The function interacts with Date through the public member function day() int weekend(Date date) { // Returns true if the date falls on a weekend return ((date. day()==0)||(date. day()==6)); }
Classes and objects • Its important to be clear about the distinction between classes and objects • It may seem obvious but it does cause confusion • Essentially an object is an instantiation of a class – We can create many objects of the same class
• We can create several date objects as follows : Date date; date. set(14, 4, 2003) Date another_date; another_date. set(15, 4, 2003); Date date Apr 14, 2003 Date another_date Apr 15, 2003
• We have 1 class – Date • We have 2 objects – date – another_date • Typically class names will start with upper case and object names will start with lower case
Creating/initializing objects – constructors • Objects can be created and initialized by declaring special public member functions called constructors with the same name as the class • A nice feature of constructors is that function overloading can be used so that objects can be initialized in different ways
class Date { private: int d, m, y; }; public: Date(); // constructor Date(int, int); // constructor Date(char *); // constructor void set(int, int); void print(); void advance(); int day() {return d; } // access function int month() {return m; } // access function int year() {return y; } // access function
• The implementation of the constructors are as follows : Date: : Date() { // default construcotr } Date: : Date(int dd, int mm, int yy) { d=dd; m=mm; y=yy; } Date: : Date(char* date_string) { // Parse the string and assign to d, m, y }
• We can now create and initialize objects as follows : Date tomorrow; // Date() called Date today(1, 9, 1998); // Date(int, int) called Date yesterday("31 st August 1998"); // Date(char*) called
• Note that the constructor is automatically called when an object is declared – It doesn’t have to be initialized (although it is usually good practice to do so) – However, the default constructor must be present if we wish to create uninitialized objects • Note also that we can create objects dynamically and this also involves calling a constructor Date* today = new Date(1, 9, 1998); today->advance();
Destroying objects – destructors • Destructors are member functions which are called automatically (see later) in order to delete an object and release all of the memory taken by that object – Unlike Java, C++ has no automatic garbage collection • For a simple class like Date, no dynamic allocation of private member variables has taken place so the compiler can delete Date objects by simply deleting each private member variable
• For classes where private members have been dynamically allocated, we must implement the destructor in order for the object to be completely deleted class my. Class { private: char* message; int message_length; }; public: my. Class(char* m, int len); ~my. Class(); // constructor // destructor
my. Class: : my. Class(char* m, int len) { message_length=len; message=new char[len]; strcpy(message, m); } my. Class: : ~my. Class() { delete[] message; }
• Destructors are called automatically when : – The delete operator is called for a dynamically created object – An automatically created object goes out of scope – The program (main) (or a function) terminates for a statically created object – The destructors for globally defined objects are called last of all
Other features of classes • We will look at a few other features of classes – this self-reference – Friends – Static class members
Self-reference - this • Each (non-static) member function has a access to a pointer to the instantiated object from which the member function was called (!!) • So, for example, in our Date class, member functions have access to a (constant) pointer of type Date*
Date private int d int m int y public. . void advance() { // access to this }.
• The following re-implementation of advance() makes a copy of a Date object using a dereferenced this but moved on 1 day Date: : advance() { Date d=*this; // make a copy d. julien++; return d; } Date d(1, 9, 1998); Date new_d=d. advance();
• Thus, a completely new initialized Date object is produced Date new_d 1/9/98 2/9/98
Friends • A function or a class can be made a friend of another class – This means that the function or class members can access the private members of the class • Using the friend keyword in a class declaration allows encapsulation to be broken and should be used carefully
class X {. friend int func(); friend class Y; . }; • This declaration allows func() and all of Y’s member functions to access the private parts of class X • Note that it is the designer of class X who specifies the friend relationships
class Date { private : int d, m, y; public : Date(int, int); // constr. . friend int weekend(Date); }; int weekend(Date date) { return ((date. d==0)||(date. d==6)); // OK! }
Static class members • Each object has its own copy of all non-static date members of a class • A static class members belongs to the class – There is only 1 class wide copy – Useful for maintaining class wide information – static class data members exist even if an object hasn’t been instantiated
• static class members still have private, public privileges – Only static public member functions can access static private members if an object has yet to be instantiated – In this case the member function is accessed through the class name using the : : operator – Once an object has been instantiated, static members can be accessed through non-static public member functions also • static class members must be initialized once and once only before use
• A static class member num_objects can be used to keep a track on the number of objects of a certain class that have been created class my. Class { private : static int num_objects; public : my. Class() { num_objects++; } }; static int get() { return num_objects; }
int my. Class: : num_objects=0; // Initialize static void main() { printf(“num objects so far = %d ”, my. Class: : get()); // prints 0 my. Class m 1; my. Class m 2; my. Class m 3=new my. Class(); printf(“num objects so far = %d ”, my. Class: : get()); // prints 3 }
And finally ……. . • The key message in this lecture is encapsulation and implementation/interface separation • This leads to self-contained software units – classes – which are the basis of object oriented programming • Object oriented applications comprise a set of objects, interacting through their class interfaces • The next lecture looks at inheritance and polymorphism which allow for flexible object behaviour, a key element of OOP
- Slides: 49