Tuesday, November 21, 2017

Lecture Notes 10

Object Oriented Programming: Lecture Notes 10

Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

We have seen how, that by default, the default constructor of the base class is used to construct the base part of the derived class this behavior can be overridden to force the base part of the derived class to be constructed using one of the overloaded versions of the base class constructors. This allows us to control how the initialization is to be carried out and in certain situations, such as when there the base class has private members it can be the only way to assign those values. To override the default behavior of the construction, initialization lists are used.

Initialization lists


An initialization list is a syntax that got introduced in C++ and its task is to initialize members of the class in a way different from simple assignment during construction time, we will talk about the difference later, but for the time being let us see the syntax so that we can anchor our discussion in a concrete piece of code. The following class uses familiar assignment to set the values of the data.

class AA{public:
     int a;
     double b;
     A(){a=0; b=2.34;}
     A(int aa,double bb){a=aa; b=bb;}
    };


The following class is identical in every respect except that it use initialization lists instead of simple assignment.

class AA{public:
     int a;
     double b;
     A():a(0), b(2.34){};
     A(int aa,double bb):a(aa),b(bb){};
    };


In the code above the member variable to left will take whatever value there is between the parentheses. A different way of achieving the above is the following.

class AA{public:
     int a;
     double b;
     A():A(0,2.34){};
     A(int aa,double bb):a(aa),b(bb){};
    };


We can list a constructor in the initialization list to make it construct the object using some specific values we wish the member variables to hold. This feature is what allows us to override the construction behavior of the base part of a derived class in the case of inheritance, because we can list one of the base class constructors in the initialization list of a derived class constructor.

class Base{public:
        int a;
        double b;
        Base(){};
        Base(int aa, double bb){a=aa; b=bb;}
      };

class Derived:public Base{public:
                int x;
                double y;
                Derived():Base(10,0.1){};
                Derived(int xx, double yy):Base(2*xx,2*yy),x(xx){y=b*yy;}
            };


The difference in behavior that we mentioned earlier lies in that assignment created the variable and then changes its value using the assignment operator =. In the case of initialization lists the values are set at the instant of creation which means that there is no assignment taking place. This has huge consequences, because, in the case of const member variables it is not allowed to change there values after creation. Therefore, the only opportunity to give them a meaningful value is at the moment of creation, and this can only be accomplished using initialization lists. The existence of private member variables of the base class is also another situation where an initialization list is the only possible way to give them meaningful values.

Polymorphism


Inheritance in OOP is the way to capture the type/subtype relationship. We have seen how inheritance makes public and protected member variables and member functions of a base class, available in its derived classes. This is sufficient enough in many situations except when the behavior of some member function of the base class is different when inherited in the derived class. Take for example the general behavior of walking. A human walks in a way that is different from an ostrich or a crocodile. Moreover, different subtypes of humans walk in different manners. For instance, a man walks differently from a woman. This fact can be captured in the OOP framework by overriding the base class function, as in the code below.

class Human{public:
        double age;
        double height;
        void walk(){cout<<"Walk like a human.";}
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };


class Woman:public Human{public:
        int xx;//Some woman attribute.
        void walk(){cout<<"Walk like a woman.";}
       };

int main(){Human h1; Man m1; Woman w1;
       h1.walk();//Output: Walk like a human.
       m1.walk();//Output: Walk like a man.
       w1.walk();//Output: Walk like a woman.
       return 0;}


When we contemplate the reality of things, we realize that the attributes of a subtype include and often add to the attributes of its type. This means that an object of some type cannot fully represent an object of one of the subtypes of the type to which it belongs. This situation is called slicing in OOP, and corresponds to assigning objects of a subclass to objects of its super-class. Object slicing is indeed undesirable. Below is an example of object slicing.

class Human{public:
        double age;
        double height;
        void walk(){cout<<"Walk like a human.";}
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

class Woman:public Human{public:
        int xx;//Some woman attribute.
        void walk(){cout<<"Walk like a woman.";}
       };

int main(){Human h1; Man m1;
        h1=m1;//Object sliced. xy is lost.
        return 0;
    }


However, we can refer to a man or a woman instance as human without contradiction. In other words we can point to an instance of a subclass using a pointer of type super-class. This is logical because, the fact that all men are human does not entail them losing their manhood and the fact that women are human does not entail them losing their womanhood. This is the rationale behind allowing the following syntax in OOP.

class Human{public:
        double age;
        double height;
        void walk(){cout<<"Walk like a human.";}
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

class Woman:public Human{public:
        int xx;//Some woman attribute.
        void walk(){cout<<"Walk like a woman.";}
       };

int main(){Human *ph1; Man m1;
        ph1=&m1;//No object copying, so no slicing
        return 0;
      }


In the code above, we can point to a man using a pointer of type human. However, the attributes specific to the class Man are inaccessible through this pointer, and this is all too natural; because referring to a man as a human should preclude us from addressing the respects in which that human is a man. But there is a small problem; when we make a man walk using the walk function, a man walks like a human rather than like a man. This is similar to changing specific behavior of an individual when referred to using a more general type to which it belongs. In other words, when we pick out a man on the street by pointing a finger at him and say "this human", he does not, magically, change his walking behavior into a more general human gate. This is unrealistic and therefore undesirable; because we know that the human pointer is in actuality pointing to a man, who walks in a manner specific to men.

class Human{public:
        double age;
        double height;
        void walk(){cout<<"Walk like a human.";}
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

class Woman:public Human{public:
        int xx;//Some woman attribute.
        void walk(){cout<<"Walk like a woman.";}
       };

int main(){Human *ph1; Human *ph2; Man m1; Woman w1;
        ph1=&m1;
        ph2=&w1;
        ph1->walk();//Output: Walk like a human.
        ph2->walk();//Output: Walk like a human.
/*Despite the existence of member functions for specific Man and Woman walking behavior, both pointers use the general walking behavior of class Human.*/
        return 0;
      }



The more logical behavior is for a man is to walk like a man and a woman to walk like a woman, even when pointed to by a pointer of a more general type. This effect can be achieved by virtualizing the functions whose behavior should change according to the type/subtype of the object a pointer is pointing to. The syntactical solution is to prepend the function by the keyword virtual.

class Human{public:
        double age;
        double height;
        virtual void walk(){cout<<"Walk like a human.";}
       };


class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

class Woman:public Human{public:
        int xx;//Some woman attribute.
        void walk(){cout<<"Walk like a woman.";}
       };

int main(){Human *ph1; Human *ph2; Man m1; Woman w1;
        ph1=&m1;
        ph2=&w1;
        ph1->walk();//Output: Walk like a man.
        ph2->walk();//Output: Walk like a woman.
/*Both pointers use the specific Man and Woman walking behavior.*/
        return 0;
      }


Now that we know how to make a class behave in the desired way, we will have to go back, and see if there is any change that needs to be done to our classes when we have inherited them, and we will find that destructors need to be declared virtual if they are to behave in the required way. If the destructor is not declared virtual, then when destroying an object through it a super-class pointer only the part of the object that comes from the super class will be destroyed. The following code illustrates the problem:

class Human{public:
        double age;
        double height;
        virtual void walk(){cout<<"Walk like a human.";}
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

int main(){Human *ph1;
       ph1=new Man;
       delete ph1; //Only the part of the object that comes from Human will be destroyed, leaving the rest of the object dangling in memory.
       return 0;
      }


The solution is to make the destructor virtual, so that the type, to which the pointer points, is checked and its version of the destructor is called.

class Human{public:
        double age;
        double height;
        virtual void walk(){cout<<"Walk like a human.";}
        virtual ~Human(){};
       };

class Man:public Human{public:
        int xy;//Some man attribute.
        void walk(){cout<<"Walk like a man.";}
       };

int main(){Human *ph1;
       ph1=new Man;
       delete ph1; //Since the destructor is virtual the type pointed to will be checked and its destructor will be used. Here the destructor of Man will be called, which in turn will call the destructor of human. leaving nothing of the object in memory.
       return 0;
      }

Thursday, November 2, 2017

Questions

Questions.
Use this post to ask questions regarding the part we have covered so far.

Friday, October 27, 2017

Lecture Notes 09

Object Oriented Programming: Lecture Notes 09

Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

The hierarchy of types


Things in the world can be of many types. The type to which something belongs, can have many levels of generality. For instance, a dog can be said to be an animal, but it is also a mammal, and is also a canine. Its being a canine does not contradict the fact that it is a mammal, because being a canine entails being a mammal. A canine has every trait a mammal has, in addition to the traits that differentiate canines from other mammals. A cat is, also, a mammal but it is not a canine; because it is differentiated by other traits that make it a feline. However, felines and canines have traits that are common to every mammals. Therefore, canines and felines are said to be similar qua mammals and different qua canines and felines. A chicken is, also, an animal and therefore it has in common with other animals the general traits of animality. However, more specifically a chicken is a bird and differentiating traits of birds are what make it a bird. This hierarchy of types is important and useful for organizing the world around us; because it allows us to extend our knowledge of the general type to whatever sub-type that comes comes under it. For example, once we know that whales are mammals we can extend our knowledge about mammals to whales. We can assert about whales that they lactate, for instance. This hierarchy of types can be captured in object oriented programming using inheritance.

Inheritance


When a class inherits another class all attributes and methods of the inherited class, become attributes and methods of the inheriting class. The inherited class is called super-class, base class or parent class and the inheriting class is called subclass, derived class or child class. Private member variables and functions of the base class are not accessible in the derived class, even though they exist in every in derived class object, because every derived class instance has a base class instance. Public members of the base class, on the other hand, are accessible in the derived class and can be used from derived objects as if they where derived class members. The syntax for the inheritance relationship is as follows:

class Base{private:
        int i;
        int func1(){return 3*i;}
       public:
        int k;
        void set_i(int m){i=m;}
       };

class Derived:public Base{private:
                int n;
              public:
                    int h;
                void set_n(int j){n=j;}
                void square_k(){k=k*k;}/*Allowed because k is a public member variable of Base*/
             };

int main(){Derived d;/*d is created and has Base members as part of it*/
       d.k=9;/*Allowed because k becomes a member by inheritance*/
       d.set_i(18);/*Allowed becasue set_i is a public member of Base and becomes a public member of Derived by inheritance.*/
       d.set_n(10);/*set_n is a public member of Derived*/
       d.square_k();/*Invokation of public member function*/
       return 0;
      }


In the code above, i and privefunc() are not accessible directly in Derived because they are private in Base. With the inheritance relationship a new access modifier called protected is introduced and its role is to make members of base classes accessible to derived classes only. Therefore, if we want to make func1() accessible to derived classes we can make it protected.

class Base{private:
        int i;
       protected:
        int func1(){return 3*i;}/*func1() is now accessible to all derived classes*/
       public:
        int k;
        void set_i(int m){i=m;}
       };

class Derived:public Base{private:
                int n;
              public:
                    int h;
                void set_n(int j){n=j;}
                int ninefold_i(){return 3*func1();}/*func1() is accessible because it is protected*/
             };

int main(){Derived d;
       d.set_i(10);
       cout<<d.ninefold_k();/*Output: 90*/
       return 0;
       }


It is important to understand that objects of derived classes have as part of them objects of base classes.

 --------+--------------------+
  ^      | Base class part    |
  |      |                    |
  |      |                    |
  |      |                    |
  |      |                    |
Derived  |                    |
Object   +--------------------+
  |      | Derived class part |
  |      |                    |
  |      |                    |
  |      |                    |
  |      |                    |
  v      |                    |
 --------+--------------------+


The order of construction of the derived class' object starts with constructing the base class part and then the derived class part. The construction of the base class part uses the default constructor of the base class, even if the derived object is constructed using a parametrized constructor. We will explain in later lectures how this behavior can be overridden.

class Base{private:
        int i;
       public:
        int k;
        void set_i(int h){i=h;}
        Base(){i=10; k=20;}
        Base(int h){i=h;}
       };

class Derived:public Base{public:
                int j;
                Derived(){j=1000;}
                Derived(int g){j=g;}
              };

int main(){Derived d1;/*i=10, k=20, j=1000|Default constructor of base is invoked*/
       Derived d2(555);/*i=10, k=20, j=555|Default constructor of base is invoked*/
       return 0;
      }







Saturday, October 21, 2017

Lecture Notes 08

Object Oriented Programming: Lecture Notes 08

Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

Below is the code for assignment 01 along with some commentary in the form of code comments. Make sure to read it through.


const double PI=3.14159;

/*The cup material and the liquid are included here as separate classes to as a design measure to make room for any future expansion of the concepts of liquid and solid material. Another design direction is to include their densities in the cup class.*/
class SolidMaterial{public: double density;
            SolidMaterial(){density=0;};
            SolidMaterial(double dens){density=dens;}};

class Liquid{public: double density;
             Liquid(){density=0;};
             Liquid(double dens){density=dens;}};

/*A cylinder object to use for cylinder proprieties; because our cup can be thought of as two coaxial cylinders.*/
class Cylinder{public: double radius; double height;
            double volume(){return PI*radius*radius*height;}
            Cylinder(){radius=0; height=0;};
            Cylinder(double r, double h){radius=r; height=h;}
        };

class Cup{private:
      /*The inner cylinder, which constitutes inner cavity of the cup*/
      Cylinder innerCylinder;
      /*The outer cylinder, determined by the inner cylinder and the cup thickness.*/
      Cylinder outerCylinder(){return Cylinder(innerCylinder.radius+thickness,innerCylinder.height+thickness);}
      double thickness;

      SolidMaterial cupMaterial;
      Liquid lq;
      double lqamount;

      public:
      /*Setting the cup attributes. The diameter is used because real life cylinders are normally given by their diameter. Using the radius instead is also fine.*/
      void setCup(double diameter, double innerht, double t, double cupmatdens){
        innerCylinder.radius=diameter/2; innerCylinder.height=innerht;
        thickness=t; cupMaterial.density=cupmatdens; lqamount=0;}
     
      /*Set liquid density. We can also use a liquid object.*/
      void setLiquid(double density){lq.density=density;}
   
      /*Fill the cup with a certain amount. If the amount exceeds the remaining capacity of the cup fill to brim.*/
      void fill(double amount){
        if(amount<=innerCylinder.volume()-lqamount){
            lqamount+=amount;
        }
        else
        {fill();}
        }

      void fill(){lqamount=innerCylinder.volume();}
      /*Empty a certain amount from the cup. If the amount to be poured exceeds the amount in the cup, empty the cup.*/
      void empty(double amount){
        if(amount<=lqamount){
            lqamount-=amount;
        }
        else
        {empty();}
        }

      void empty(){lqamount=0;}
      /*Take note of how we got the outer cylinder volume. Try to explain it to yourself.*/
      double tare(){return (outerCylinder().volume()-innerCylinder.volume())*cupMaterial.density;}

      double lqweight(){return lqamount*lq.density;}
   
      double weight(){return tare()+lqweight();}

      Cup(){setCup(0,0,0,0); lqamount=0;}
      /*Construct an empty cup*/
      Cup(double diameter, double innerht, double t, double cupmatdens, double lqdens){
        setCup(diameter,innerht,t,cupmatdens); lqamount=0; lq.density=lqdens;}
      /*Construct a cup that has a certain amount of liquid*/
      Cup(double diameter, double innerht, double t, double cupmatdens, double lqdens, double amnt){
        setCup(diameter,innerht,t,cupmatdens); fill(amnt); lq.density=lqdens;}

    };

Thursday, October 19, 2017

Lecture Notes 07

Object Oriented Programming: Lecture Notes 07

Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

 

Arrays contd.


Arrays of objects can be allocated statically and used in the way familiar to C programmers. The default constructor is automatically used when creating the objects that constitute the elements of the array.

class AA{public: int d,e;
     int mul(){return d*e;}};

int main(){AA aaar[10];
    aaar[7].d=10;
    aaar[7].e=11;
    cout<<aaar[7].mul();//Output: 110
    }


Arrays can be allocated dynamically, as well. This is accomplished using the new keyword. Dynamically allocated arrays must, eventually, be explicitly deleted. The example below illustrate the syntax for accomplishing dynamic allocation. Only the default constructor can be used for the dynamic allocation of arrays.

class AA{public: int d,e;
     int mul(){return d*e;}};

int main(){AA *paa;
   
    paa=new AA[10];/*Dynamic allocation of an array of type AA of size 10*/
    paa[7].d=10;
    paa[7].e=11;
    cout<<paa[7].mul();//Output: 110
    delete [] paa;
    return 0;
    }


Since in the case of arrays, only default constructors are allowed, populating arrays is done in a later step. The code below uses a loop to populate a dynamically allocated array, and then displays the result on the console using another loop:

class AA{public: int d,e;
     int mul(){return d*e;}};

int main(){AA *paa;
   
    paa=new AA[10];
   
    int i;
    for(i=0;i<10;i++){cin>>paa[i].d; cin>>paa[i].e;}

    for(i=0;i<10;i++){cout<<paa[i].mul();}

    delete [] paa;
   
    return 0;
    }


Copy constructors


Copy constructors are used to create objects that are exact copies of other objects. A full treatment of the syntax involved in defining copy constructors can be somewhat complicated at this stage, but the basic idea is simple. A Copy constructor takes one object of same type of the class as a parameter, and copies each member variable of the passed object to the corresponding member variable in the object being created. As such, the prototype of the copy constructor for some class AAA should look like this AAA(AAA). In practice, however, the mentioned copy constructor's prototype looks like this AAA(const AAA&). The const keyword here tells the compiler that the object passed is constant and therefore should be immutable. The importance of the const keyword in this context becomes clear when we consider the role of the ampersand (&). The ampersand here is different from the ampersand used to get a pointer of an object or a variable, and is confusingly called reference in the context of C++. It is an addition to C++ that forces a function -in this case the copy constructor- to use the original object or variable in its original memory space rather than copy it to the function's memory space. This feature is especially important in OOP; because most of the time we find ourselves dealing with large objects, which are expensive to copy from one memory space to another. But allowing a function to work on the original object exposes it to modification. Therefore, the keyword const is prepended to it to prevent such a any modification from taking place. The copy constructor is supplied by the compiler and need not be overridden unless there is a plausible reason to do so. One such typical reason is having a deep class, whose pointer member(s) referent(s) need to be copied explicitly in the overridden copy constructors to avoid having a shared referent. The following code correctly uses the default copy constructor for a shallow class:

class AA{public: int d,e;};

int main(){AA aa1;
    aa1.d=33; aa1.e=99;
   
    AA aa2(aa1);

    cout<<aa2.d<<' '<<aa2.e;//Output: 33 99

    return 0;
    }



The diagram below illustrates a deep object aa1 of type AA after assigning values to members and member(s) of pb referent object. Note that the code below is defective since it does not override the copy constructor:

class BB{public:int x;}

class AA{public: int d,e;
     BB *pb;
     AA(){pb=new BB();}
     ~AA(){delete pb;}
    };

int main(){AA aa1;
    aa1.d=10; aa1.e=30;
    aa1.pd->x=70;
    return 0;
    }

aa1                    Obj1 BB@address 16000
+------------+    +--->+--------+
|            |    |    |        |
| d: 10      |    |    | x: 70  |
|            |    |    +--------+
| e: 30      |    |
|            |    |
|pb:16000+--------+
|            |
+------------+


Here is the situation we will end up with when using the default copy constructor for a deep class. It is evident that this situation leads to all kinds of problems because of the shared referent. Consider what would happen when aa1.pb->x is changed. Since we have the same referent, aa2.pb->x will be changed as well. Even worse, in the case of dynamically allocated objects, when the first object is deleted the other object will have a pointer pointing to a deallocated memory space.

class BB{public:int x;}

class AA{public: int d,e;
     BB *pb;
     AA(){pb=new BB();}
     ~AA(){delete pb;}
    };

int main(){AA aa1;
    aa1.d=10; aa1.e=30;
    aa1.pd->x=70;

    AA aa2(aa1);
    return 0;
    }

aa1                    Obj1 BB@address 16000
+------------+    +-->>+--------+
|            |    | |  |        |
| d: 10      |    | |  | x: 70  |
|            |    | |  +--------+
| e: 30      |    | |
|            |    | |
|pb:16000+--------+ |
|            |      |
+------------+      |
                    |
                    |
aa2                 |
+------------+      |
|            |      |
| d: 10      |      |
|            |      |
| e: 30      |      |
|            |      |
|pb:16000+----------+
|            |
+------------+


To solve the problem above, we need to override the copy constructor to perform a deep copy when we have deep classes. See the code and the diagram below:

class BB{public:int x;}

class AA{public: int d,e;
     BB *pb;
     AA(){pb=new BB();}
     AA(const AA& aa){d=aa.d; e=aa.e; pb=new BB(*aa.pb);}
     ~AA(){delete pb;}
    };

int main(){AA aa1;
    aa1.d=10; aa1.e=30;
    aa1.pd->x=70;

    AA aa2(aa1);
    return 0;
    }

aa1                    Obj1 BB@address 16000
+------------+    +--->+--------+
|            |    |    |        |
| d: 10      |    |    | x: 70  |
|            |    |    +--------+
| e: 30      |    |
|            |    |
|pd:16000+--------+
|            |
+------------+


aa2                    Obj2 BB@address 24000
+------------+    +--->+--------+
|            |    |    |        |
| d: 10      |    |    | x: 70  |
|            |    |    +--------+
| e: 30      |    |
|            |    |
|pd:24000+--------+
|            |
+------------+


Design consideration when a value is determined by other member variables


When deciding which member variables to include in a class, it is important to keep in mind the following guiding principle:
If some information about the object can be determined by other member variables this information should not be included as a member variable but rather it should be calculated from other member variables. For example, assume that we want to design a rectangle class, and among the information that we wish to be able to retrieve from our class is the area and the perimeter of the rectangle. In such a class, if we include the area and the perimeter as member variables our design would be flawed because the area and the perimeter can be calculated by knowledge of the side lengths of the rectangle. Here is the flawed class:

class Rectangle{public:
        double height;
        double width;
        double area;
        double perimeter;
        };


The reason that this design is flawed is because it is prone to inconsistent values of the height, length, area, and perimeter. We can have an object that has 5 for height, 10 for width, 70 for area and 40 for perimeter. Making area and perimeter functions that perform a calculation of the area and perimeter and return their values circumvents the aforementioned problem. The correct design should be as follows:

class Rectangle{public:
        double height;
        double width;
        double area(){return height*width;}
        double perimeter(){return 2*(height+width);}
        };


That is why when we designed a Triangle class and chose to determine a triangle by the lengths of the three sides, we did not include member variables for area and perimeter. Nor should we include member variables for the triangle's angles, since any angle can be calculated by knowledge of the side lengths of the triangle's sides which are included as member variables in the class.

Monday, October 16, 2017

Lecture Notes 06

Object Oriented Programming: Lecture Notes 06

Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

Destructors


We have seen that constructors are used to create objects. When execution exits the scope of the object, the object is destroyed. The destruction part is done by the destructor, which can be overridden to control how an object is destroyed.

class A{public: int k;};

int main(){A a1;
   
    return 0;/*Destructor is called here as scope exits when main returns*/
    }


As with constructors, every class is supplied with a default destructor. If not overridden the default constructor will simply release the memory occupied by the object. It should be noted that, in contrast to constructors, destructors cannot be overloaded, which means that there is always one destructor that takes no arguments. The code below illustrate the syntax of overriding the default destructor:

class A{public: int k;
    ~A(){cout<<"Object is destroyed\n";}
    };

int main(){A a1;
   
    return 0;/*Output: Object is destroyed*/
    }


It should be clear that destructors are the most unlikely place to communicate with the user through the console, and is done here for illustration purpose only. A good and typical use of destructors will be mentioned below when we consider deep objects. To understand what a deep object is, we need to make understand how pointers work in C++.

Pointers and the new and delete operators


As every C programmer knows, pointers are memory addresses that refer to the location of a variable. In addition to C-like pointers, pointers to objects also exist. When declaring a pointer to a basic type (float, int, char ..etc.) the referent of the pointer is created along with the pointer. However, when a pointer to an object is declared the referent of the pointer needs to be created independently. The keyword new is reserved for this purpose in C++. It creates an object and returns a pointer to the object. To access member variables and member functions of the object the arrow operator -> is used in the case of pointers. Below is an example of the use of the new operator.

class A{public: double x,y;
        double mul(){return x*y;}
    };

int main(){A *pa; /*Declares a pointer to an object of type A. The object does not exist yet.*/
      
       pa=new A(); /*An object of type a is created and its address is returned.*/
       pa->x=3.2;
       pa->y=0.2;
       cout<<pa->mul();//Output: 0.64
       /*There is something missing here*/
       return 0;
    }


This code, however, will cause compilation error because we are attempting to access a member of a non-existent object:

class A{public: double x,y;
        double mul(){return x*y;}
    };

int main(){A *pa; /*Declares a pointer to an object of type A. The object does not exist yet.*/

       pa->x=18; /*Compilation error. The object is not existent yet.*/
       return 0;
    }


Objects created using the new operator are said to be dynamically allocated because they are allocated at the run-time. For this reason, they should be deallocated when no longer needed. This is accomplished using the delete operator and is normally placed at the end of the scope where the object was created. Failing to delete dynamically allocated objects results in memory leaks, with all their concomitant problems. It is important to realize that C++ does not implement automatic garbage collection as in other languages (e.g. Java); because automatic garbage collection leads to a less efficient code. C++ also allows for more control at the side of the programmer. The code above is, therefore, produces a memory leak because the object was not deleted. Here is the code after fixing it:

class A{public: double x,y;
        A(double u, double w){x=u;y=w;}
        double mul(){return x*y;}
    };

int main(){A *pa; /*Declares a pointer to an object of type A. The object does not exist yet.*/
      
       pa=new A(); /*An object of type a is created and its address is returned.*/
       pa->x=3.2;
       pa->y=0.2;
       cout<<pa->mul();//Output: 0.64
       delete pa;/*Object deleted*/
       return 0;
    }


What the delete operator actually does, is invoke the class destructor. Take for example the following code:

class A{public: double x;
        ~A(){cout<<"Bye Bye";}
    };

int main(){A *pa; /*Declares a pointer to an object of type A. The object does not exist yet.*/
      
       pa=new A(); /*An object of type a is created and its address is returned.*/

       delete pa;/*Output: Bye Bye*/
       return 0;
    }


This brings us to one of the typical usages of destructors; namely deep classes and their objects.

Deep classes/objects


A deep class is a class that has one or more pointers member variables. The presence of such member variables mandates extra care when designing the class. First, the objects to which the pointers should point need to be created dynamically, usually, somewhere in the class. Those dynamically created objects, need to be destroyed when the object whose member pointers point to them is destroyed. Otherwise, they will stay hanging in memory and cause a memory leak. Below is an example of a deep class, together with the proper design of the destructor.

class AA{public: int d,e;};

class BB{public: int f,g;
    AA *a;
    BB(){a=new AA();}
    ~BB(){delete a;}
};


Indeed, a class can be of any depth. If the dynamic creation and deletion of objects is taken care of, the design remains the same.


class AA{public: int d,e;};

class BB{public: int f,g;
    AA *a;
    BB(){a=new AA();}
    ~BB(){delete a;}
};

class CC{public: int h,i;
    BB *b;
    CC(){b=new BB();}
    ~CC(){delete b;}
    };


There are more considerations, to pay attention to, when designing deep classes, but we will leave them for the time being.

It is important, to mention in that member arrays do not lead to deep objects. Therefore, the class below is a shallow class.

class AA{public: int d,e;

          int ar[4];};/*This array is inside the bounds of the class*/

And class DD is shallow as well.

class AA{public: int d,e;};

class DD{public:
        AA aaar[4];
    };






Friday, October 13, 2017

Assignment 01

Assignment 01


Author: A. El-Gadi/Faculty of Computer Engineering/Tripoli University

A simplified cup can be modeled as a cylinder of uniform thickness and a base that has the same thickness. Both the cylinder and the base are made of the same material, which is given by its density. The cup can be filled with a certain amount of liquid -again given by its density-, and a certain amount of the liquid can be poured out of the cup. There should be a function to get the tare of the cup, a function to get the weight of the liquid in the cup and a function to get the overall weight of the cup and the liquid. There, should also be a function to fill the cup with a certain amount of liquid and a function to pour a certain amount of liquid. If the amount of liquid is not specified when filling the cup, the cup should be filled to the brim, and if the amount of liquid to be poured is not specified the cup should be emptied. When created a cup should be empty, but it can also be created with a certain amount of liquid in it. Write a class that captures the concept of a glass as described above making sure to disallow any illogical and inconsistent values for the different variables and parameters in your class.

Assignment due on Tuesday, October 17, 2017