第十五天(類繼承 · 二)

        毋需多言,直接粘貼。今天這篇筆記寫得好辛苦,定有不少錯誤。哎,甭管了,英文~~,Let it go...


2011-11-25(Class Inheritance II)
1. Access Control: protected

A protected members of a class can be only accessed by itself and its derived class. Using protected data members instead of private may simplify writing the code, but it has a design defect. For example, continuing with the
AClass and subA class, if subA has a following public function:

void subA::Reset(int a)
{
    aMembet1 = a;
}
           The AClass class was designed so that the setMember1() function provide the only means for user to modify its data member. But this public method essentially makes aMember1 a public variable, which goes against the OOP aim.
       
 So when you design a class, you should prefer private to protected access control for class data member. Using base-class methods to provide base-class data. Protected members of class are more likely designed as functions
2 . Abstract Base Classes(ABC)
Abstract is a concept of OOP, which is muc
h more systematic way to implement inheritance and polymorphic. Let's get a little bit of information of ABCs.
I. Why we need ABC.
Suppose you have to design two classes called
classOne and classTwo. In order to describe their relationship, we divide classOne's members into two part called part1 and part2. Similarly classTwo is divided into part3 and part4. The relationship of these four part is part1 = part3, part2 != part4. 
          How to declare this two classes? If you declare them independently, you ignore the fact that they have a lot in common. If you let
classTwo inherits from classOne(Or classOne inherits from classTow), you ignore the fact that they have different part and how to deal with it?
         The solution is to declare another class called
classThree, which declares and defines the common part. This is the rudiment of ABCs. In general, we can sum up the common part of a considerable number of classes, and use the common part to construct a class. After creating this abstract class, you can inherit it if you find you are going to design a class that has this common part. 
II. How to implement ABCs
Assume that
classOne and classTwo are like this:
class classOne
{
private:
    int common1;
    int common2;
    int different1;
public:
    classOne();
    void commonf1();
    void commonf2();
    void differentf1();
    void showMembers();
};

class classTwo
{
private:
    int common1;
    int common2;
    int different2;
public:
    classTwo();
    void commonf1();
    void commonf2();
    void differentf2();
    void showMembers();
};
Then classThree must like this:
class classThree //an ABC
{
private:
    int common1;
    int common2;
public:
    classThree();
    void commonf1();
    void commonf2();
    virtual void showMembers() = 0;
};
void classThree::commonf1(){/*function body*/}
void classThree::commonf2(){/*function body*/}
Later on, let classOne and classTwo inherit from classThree, and then define their own functions. In the classThree, a virtual function follows with the symbol: “= 0”. Next section, we will discuss it.
3. ABC symbol: “
= 0
Virtual function has
= 0 at the end if its declaration is termed pure virtual function. It is pure virtual function that makes a ordinary become abstract class. In other word, ABC at least has a pure virtual function. We can see that showMembers() work differently for two classes, that is the reason we make it pure virtual function.
        When a class declaration contains a pure virtual function, you can't create an object of that. The idea is that ABCs solely to serve as base classes. Once a class inherits from an ABC, you must be rewrite the pure virtual functions.
4. Inheritance and Dynamic Memory Allocation
If a base class uses dynamic memory allocation an redefines assignment and a copy constructor, how does that affect the implementation of the derived class? The answer depends on the nature of the derived class: derived class itself use or not use dynamic memory allocation.
I. Derived Class Doesn't Use
new 
Suppose you begin with the following base class the uses dynamic memory allocation:
class baseClass
{
private:
    char* bcMember;
public:
    baseClass();
    virtual ~baseClass();
    baseClass(baseClass& bc);
    baseClass& operator= (baseClass&);
};
Now suppose you derive a class that does not use new and delete :
class derivedClass : public baseClass
{
private:
    char str1[40];
    std::string str2;
public:
    //...
};
It's not necessary that define an explicit destructor, copy constructor and assignment operator. To explain it, we must know about:
         #      Derived class objects consist of base class objects and its new data members;
         #      Memberwise copying dose not create problems if it apply to basic data type(including structures).
        First, destructor, if you don't define one, the complier will define a default destructor that does nothing. When we need to destroy a derived class object, destructor of base-class will be called to destroy base-class data member and destructor of derived-class will be also called to destroy derived-class data member. Both of this two behavior result in no problem.
        Next copy constructor, the same as destructor. When copy a derived class object, copy constructors belong to base-class and derived-class would copy their own portion of derived-class object. Both of this two behavior create no problem. Essentially the same situation holds assignment. 
       These properties of inherited objects also hold true for class members that are themselves objects. Like the data member of
derivedClass str, which is actually an object of string and string class also uses dynamic memory allocation. When you create, destroy, copy or assign the derived-class object, the compiler will calls the constructor, destructor, copy constructor and assignment of string class automatically.
II. Derived Class Does Use new
Suppose that the derived class uses new:
class derivedClass : public baseClass
{
private:
    char* str; //use new in constructors 
public:
    //...
};
In this case, of course, you do define an explicit destructor, copy constructor and assignment operator for derived class. Let's consider these methods in turn:
i. Destructor
A derived class destructor automatically calls the base-class destructor, so its own responsibility is to clean up after what the derived class constructor do:
baseClsss::~baseClass()     //takes care of baseClass stuff
{
    delete[] bcMember;
}
derivedClass::~derivedClass()//takes care of derivedClass stuff
{
    delete[] str;
}
ii. Copy Constructor 
Copy is another way to create object. But when you define derived-class copy constructor you can not access to base-class private data member. So it must invoke the base-class constructor to handle the base-class share of the data:
dericedClass::derivedClass(const derivedClass& dc:baseClass(dc)
{
    str = new char[std::strlen(dc.str) + 1];
    std::strcopy(str,dc.str);
}
Note that copy constructor of base-class was called in the definition of derived-class copy constructor ,whose task is to copy the base-class portion of dc to the calling object of derived-class.
iii. Assignment Operator
Similarly, an explicit assignment operator for a derived class also has to take care of assignment for the inherited base class object. You can explicitly calling the base class assignment operator to accomplish this.
derivedClass& derivedClass::operator =(const derivedClass& dc)
{
    if(this == &dc)            //if the calling object assigns to itself
        returm *this;
    baseClass::operator =(dc); //assign the base portion

    str = new char[std::strlen(dc.str) + 1];
    std::strcpy(str,dc.str);
    return *this;
}
The statement
                 baseClass::operator =(dc);
is equals to
                 *this = dc;
Note that, when calling the assignment operator of base class, it completes the task of delete the old object members.
         In summary, when both the base class and derived class use dynamic memory allocation, the derived-class destructor, copy constructor and assignment operator all must use their base-class counterparts to handle the base class component. This common requirement is accomplished three different ways. 
5. Friends Between Base-Class and Derived-Class
Let's think about an example:
class baseClass
{
    //… …
    friend std::ostream& operator <<(std::ostream& os, const baseClass& bc
    //… …
};
class derivedClass
{
    //… …
    friend std::ostream& operator <<(std::ostream& os, const derivedClass& dc);
    //… …
};
//implementation
std::ostream& operator <<(std::ostream& os, const derivedClass& dc)
{
    os << (const baseClass&)dc;
    //… other statement
}

A friend of derived-class is not the friend of base-class. So how to access the private data member of base-class? The solution is to use the friend functions of base class when implement the derived-class friend. Using a type cast so that prototype matching will select the correct function.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章