C++ PROGRAMMING LANGUAGE: CLASSES. CAAM 519, CHAPTER 13 This chapter focuses on introducing the notion of classes in the C++ programming language. We describe how to create class and use an object of a class. We also provide details on the implementation of function that interact with the member of a class and the overloading of operator such that they can operate on a class s object. 1. Introduction to classes and objects The notion of class in C++ is one of the main feature that distinguish C++ from the C language programming. A class allows to define a new type, i.e. data structure, that is used to define objects. Such data structure is similar to the one that can be created with a structure in C. The main difference is that a class can contains functions that are often referred as methods. We note that structures in C++ can also have methods as members, the main difference with a class is that the members of a structure are set to public by default (see section 1.1 for more information on private, protected and public members). Such methods can be used to initialize data of the class (constructor), destruct data that are allocated dynamically (destructor) or perform operations involving the class s data or other objects of the class. The data and methods of a class are called the class s members. In this section, we show how to create a class and an object of a class. Then we describe the implementation of a class s method that operates on a class s data. Eventually, we also give details on the implementation of constructor/destructor of a class and on the use of pointers of class. 1.1. Definition of classes. A class has the following structure: class Vector { public : int length ; double * vec ; ; The declaration of a class and its member (data or methods) should be done in a header file (with proper header guard) such that it can be used by other files modulo an include. The actual definition of the methods (functions) should be implemented in a source file. However we will see later in the course that when using template of class, the declarations and definitions of a class and all its members is done in a header file. An object of the class Vector is created as follows. The object Vec1 has access to all the public members of the class Vector. These member (data and methods) can be accessed with the operator. 1
2 Vec1. length =3; A class can also have two other types of members which are: private. Can only be accessed in the class. protected. Can only be accessed in the class and by derived class (see next chapter). By default a member of a class is considered private. Such that, in the above example every members defined before the instruction public: would be considered as being private members. Members of a class can be declared as static. In that case, all the objects of the considered class share the same static member. Meaning, if an object set the value of a static integer member to one, all the other object of this class considered the static integer to be one. It is possible to see an analogy with a static member of a function that keep it value between invocations. 1.2. Add function/method in a class. Unlike with structures, functions can be declared as a member of a class. To include a function in a class a programmer should: declare the prototype of the function in the class (header file). Do not forget to declare it as a public member if you want to access it outside the class (this is not mandatory). define the function in a source file that include the header file where the class is defined. To define a member function of a class, named Vector, in a source file we have to add Vector:: before the name of the function. For instance, if we want to create a function GetLengthVector that returns the length of an object of the class Vector, we would write the following instructions: int Vector :: GetLengthVector ( void ){ return length ; Assuming the above method is a public member of the class Vector, it can be called in a source files as follows: Vec1. length =3; int i= Vec1. GetLengthVector (); Note that the functions of a class are only accessible via an object of the same class (with the operator. like for the data member of the class). We refer to the example ex28 class vec.cc for more information. Remark: The definition of a class s function can be done in the class definition, meaning in the header file that contains the class. However such implementation is not encouraged as it increases the time needed to compile the program s executable. If the functions are defined (not only declared) in the class instructions, they are compiled each time a source file includes the header file that contains the class. If the function is defined in a source file, its instruction is compiled only one time. This is the same argument that drives us to declare classic functions s prototypes in header files and write their instruction in a source file.
C++ PROGRAMMING LANGUAGE: INTRODUCTION. 3 1.3. Friend function. The C++ programming language allows to create function that have access to all the members of a class without being a method of the class. Such functions are called friend function. The declaration of their prototype should be done in the class by adding the attribute friend at the beginning of the function s declaration. The definition of the function is done in source files the same way a classic function is defined. For instance, let s create a GetLength friend function of the class Vector defined previously such that it takes an object of Vector as entry and return the object s length. First, we need to add the following prototype in the class vector: public : friend int GetLength ( Vector &); Then in a source file, we can add the definition of GetLength as follows: int GetLength ( Vector & Vec1 ){ return Vec1. length ; Friend functions are mainly used to create functions that have to access to private and protected members of a class without being a method of the class. 1.4. Constructor and destructor. A class presents two kind of methods, referred as contructor and destructor, that are either executed when an object is created (constructor) or when an object goes out of scope, i.e. when it is destroyed (destructor). The name of a constructor is the name of the class while the name of a destructor is the name of the class preceded by a tilde. Their syntax are the following: NameClass (){ // instructions // define member of class NameClass NameClass (){ // Instructions It is possible to overload the constructor method so that it takes arguments that can be used to initialized certain members of the class s object. Here is an example of constructors of a class that has only one integer member called my int. ExampleClass (){ my_int =0; ExampleClass ( int my_int =a; a){ If the programmer does not implement specific constructor and destructor, a class still has a default constructor and destructor. When defining an object of such class, the default constructor allocates the required memory to store the members of the object (declared in the class). The default constructor, called when the object goes out of scope, deallocates the memory associated
4 to these members. Note that if a class has a pointer as member and that the programmer allocates dynamically some memory to the pointer, only the first block of memory is freed by the default constructor. So one need to implement a specific destructor. Here is an other example that uses the class Vector defined previously. We declare the following two constructor and a destructor in the class Vector. class Vector { public : int length ; double * vec ; // Constructors Vector (); Vector ( int ); // Destructor Vector (); ; Then we need to define the instructions associated to these methods in a source file. Vector :: Vector (){ length =5; // default value vec = ( double *) calloc (5, sizeof ( double )); Vector :: Vector ( int n){ length =n; vec = ( double *) calloc ( n, sizeof ( double )); // Destructor Vector (){ if(vec!= NULL ){ free ( vec ); The above instructions set a length of five when an object of the class Vector is created without specifying a length in its declaration. Here is how a programmer would create objects of the class Vector. // length 5 // Vector Vec2 (); // compilation error Vector Vec3 (4); // length 4 1.5. Pointer to a class. Pointer of class works the same way that pointer of structures. Meaning that they can be created by adding the key * in their declaration. The members of the class they point can be accessed with the operator ->. Here is a short example that summarizes how to create and use a pointer of a class: Vector * pt_ Vec ;
C++ PROGRAMMING LANGUAGE: INTRODUCTION. 5 pt_vec =& Vec1 ; pt_vec -> length =4; where the class Vector is the class defined in the section 1.1. To facilitate the implementation of some method of a class and overload operator (see section 2), all classes in C++ can referred to the current object by using a pointer named this. For instance, in the constructor of the class Vector introduced earlier, length could be replaced by this->length. 1.6. Synthesis via an example. This example is available on CAAM519 s webpage, it is named ex29 class box.cc. It summarizes the features of a class in C++ introduced previously. This example introduces a class named Box that contains: two private data of type integer named width and height. two constructors (one default and one with two integers as inputs). a function VolumeBox that computes the volume of the Box. Note that the width and height of a Box object can not be accessed outside the class Box as they are defined as private members. To face this issue, the class also contains two functions named WidthBox and HeightBox that returns the width and height of an object of the class Box. We refer to the example for more details on how to implement this class and use objects of the class Box. 2. Overloading Operators In addition to the overloading of function, the C++ programming language also allows to overload operators such as the addition +, multiplication *. Only four operators can not be overload which are., ::,?: and.*. The overloading of an operator is done using the following instructions. type_ output operator name_ operator ( type_ inputs ){ // instructions This feature is used to introduce new operators that involve one or more objects of a class or different classes. We note that it can also only involve a variable of type enumeration that are created with the keyword enum. Using the class Vector introduced previously, we can overload the operator () to have acces to an element of a object of Vector. It would be done as follows. double & operator ()( int index ){ return vec [ index ]; where vec is the pointer that refers to the first element of the vector. The operator () can then be used to read or set the value of an element of an object of the class Vector. Vector Vec1 (4); double d = Vec1 ( 0); Vec1 (3)=5.0; Note that the last line of the above example is only possible because the ouput of the overload operator () returns a reference. It is also possible to overload operator to do operation (like addition) on different objects of a class (and even different classes). Here is an example where we overload the operator addition to sum two vectors of the class Vector.
6 Vector operator +( const Vector & Vec ){ Vector Vec_out (this -> length ); for ( int i =0; i< Vec_out. length ; i ++){ Vec_out. vec [i] = this -> vec [i] + Vec. vec [i]; return Vec_ out ; We refer to the example ex30 classs overload operator.cc for more information. Remark: To avoid memory overread/overwrite, a programmer should add some tests in the above examples to be sure that the added vector have the same length or that the indexes are in the good interval. We also note that C++ contains a standard library called vector that allows to create vector and operate on them. This library will be introduced later in the course. 3. The rule of three When a user defines explicitly a destructor, a copy constructor or a copy assignment operator (equal operator) in a class, it is advised to define explicitly the three. This rule, called rule of three, allows to avoid unexpected behavior that can be generated by the default destructor, default copy constructor and default copy assignment operator that are generated when creating a class. The implementation of these members of a class requires to follow certain guidelines, such as the use of const reference as arguments for the copy constructor/operator. The copy assignment operator should also check for self assignment (a=a) and it should return a reference to allow chaining assignment. We refer to the class and the example ex my vec.tar.gz for more information on how to implement a copy constructor and a copy assignment operator. 4. Note on implementation strategy The implementation of classes in a C++ program should be done with the reminder that a class may be used by a large various of source files and that the class may go through modification/improvement as the project evolves. As a consequence, the modification of a class should have none or limited consequences on how object of the classes are used in the source file of the program. Such classes present good data abstraction and encapsulation. It means that: the interface used to call a methods of a class does not depends of the implementation of the method. Meaning we should be able to modify a method s instructions without modifying the input/output of the methods. the access of members of a class outside the class (public member) should be limited to members that represent a feature (not just a variable) of the class. For instance, constructing an object, destructing an object, setting the value of the ith element of a vector or setting the length of a vector (and realloc it). By default it is preferable to declare all the data of a class as private (or protected). Then the programmer has to decide which methods should be public or not. When defining a new class, a programmer should always think about the above points such that he can later modify its class without modifying (or debugging) all its source files.