第十七天(C++代碼重用II)

       前幾天裝了個win7 x64(D版),無他,一爲圖個新鮮,二爲告別硬盤的遊戲(雖然裝系統不影響非系統盤),寓爲新的開始。也正因爲這個x64,導致我今天下午開的網銀死活不能初始化U盾……可能是64位的原因,可能是IE9的javascript不太支持(不太清楚),一個下午。最終只能借用宿舍電腦。

      繼續填坑。


2012-3-16(Reusing Code in C++ part II)

4. Private Inheritance

I.Big Picture

C++ has a second means of implementing the has-a relationship: private inheritance.With private inheritance, public and protected members of the base class become private members of the derived class. Public interface of base class can only be used by inside the member functions of the derived class. Just like contained classes we discussed no long ago: the public functions of contained classes can be invoked by their object and contained classes' object (like name ) is usually qualified private.

Private inheritance, unlike public or protected inheritance, is a kind of has-a relationship not is-a. With public inheritance, the public methods of base class become public methods of the derived class. That means what base class does derived class can do, so derived class is-a-kind-of base class. With private inheritance, you can imagine derived class has the objects of base class, and because you can invoke public interface of base-class, which is similar to you can invoke public methods of base class through its object in the version of contained objects.

II.Syntax

To get private inheritance, you use the keyword private instead of public when defining the class:      

                     class Student: private std::string, private DbArray

                     {}

By the way, private is the default. so omitting an access qualifier also leads to private inheritance.

 

5. Transform Contained Objects Into Private Inheritance Version

I.Initializing Base-Class Components

Having implicitly inherited components instead of member objects affects the coding of this example because you can no longer use name and scores to describe the objects. The new version can also use the initializer list syntax by using the class name instead of a member name:

                        Student(const char* str,const double* d, int n)

                          :string(str), DbArray(d,n)

With this technology, we can redefine Student class as follows:

#ifndef STUDENT_H_
#define STUDNET_H_

#include <iostream>
#include <string>
#include <valarray>

using std::string;
using std::valarray;
using std::ostream;
using std::istream;

class Student: private string, private valarray<double>
{
private:
        typedef valarray<double> DbArray;
        void sco_out() const;    
public:
        Student(): string("Null"),DbArray() {}               //default constructor
        Student(const string& s): string(s),DbArray() {}//create student without score
        //create student without name
        explicit Student(int n): string("Null"),DbArray(n) {}
        //student has given name and empty score
        Student(const string& s, int n): string(s),DbArray(n) {}
        //student has given name and given scores
        Student(const string& s, DbArray d): string(s),DbArray(d) {}                    //by valarray object
        Student(const string& s, const double* d, int n): string(s),DbArray(d,n) {} //by ordinary array
        ~Student(){}
        /******** I/O system ********/
        void setName(const string& s);
        void setScores();
        const string& Name() const;	
        double average() const;
        double operator [](int i) const;
        double& operator [](int i);
        //accept one word
        friend istream& operator >>(istream& is, Student& st);	
        //overload the getline() function,accept one line
        friend istream& getline(istream& is, Student& st);			
        friend ostream& operator <<(ostream& os, Student& st);
};
#endif
The only changes are the omission of explicit object names and the use of class names instead of member names in the inline constructor.

II. Accessing Base-Class Methods

Let's discuss about the function:average().As with containment, the technique for implementing is to use the valarray size() and sum() methods. Inheritance can't do that, but C++ offers another way: using the class name and the scope-resolution operator to invoke base-class methods:

double Student::average() const
{
       if(DbArray::size() == 0) return 0
       returnDbArray::sum() / DbArray::size()
}
III. Accessing Base-Class Objects

The scope-resolution operator allows you access to a base-class method, but what if you need the base-class objects itself? Like the Name() member function, which requires return the string object name. To do that, you can use a typecast. Because Student is derived from string, it's possible to type cast a Student object to a string object. Recall that the this pointer points to the invoking object, so*this is the invoking object. To avoid invoking constructors to create new objects, you use the type cast to create a reference:     

const string& Student::Name() cons
{
      return(const string&)*this; 
}
IV. Accessing Base-Class Friends

With type cast, you can easily implement derived-class friends byaccessing base-class friends, like: 

ostream& operator <<(ostream& os, Student& st
{
      os <<(string&)st << "'s scores:\n"
      ...
}
If MC is a Student object, then the statement

                                     cout << MC;

invokes that function. Then MC is transform into string object and that matches the operator <<(ostream&,const string&) function.

V. Implementation Design (V2.0)

#include "Student.h"

using std::endl;
using std::cout;
using std::cin;

void Student::sco_out() const
{
	int size = DbArray::size();

	if(size == 0) cout << "Empty Score\n";
	else
	{
		for(int i = 0; i< size; i++)
		{
			cout << DbArray::operator[](i) << " ";
			if(i % 5 == 4) cout << endl;
		}
		if(size % 5 != 0) cout << endl;
	}

}

void Student::setScores()
{
	int size = DbArray::size();
    cout << "Input " << (const string&)*this << "'s scores: \n";
	for(int i = 0; i< size; i++)
	{		
		cout << "#" << i + 1 << ": "; 
		cin >> DbArray::operator[](i);
	}
}

const string& Student::Name() const
{	
	return (const string&)*this;	
}

double Student::average() const
{
	if (DbArray::size() == 0) return 0;
	return DbArray::sum() / DbArray::size();
}

double Student::operator [](int i) const
{	
	return DbArray::operator[](i);	
}

double& Student::operator [](int i)
{
	return DbArray::operator[](i);
}

istream& operator >>(istream& is, Student& st)
{
	is >> (string&)st;
	return is;
}

istream& getline(istream& is, Student& st)
{
	getline(is,(string&)st);
	return is;
}

ostream& operator <<(ostream& os, Student& st)
{
	os << (string&)st << "'s scores:\n";
	st.sco_out();
	return os;
}

VI. Testing Design (V2.0)

Note that the two versions of the Student class have exactly the same public interface, so you can test the two versions with exactly the same program. Here, I won't paste again. (Part I available)

 

6. Containment Or Private Inheritance

Given that you can model a has-a relationship either with containment or with private inheritance, which should you use? Most C++ programmers prefer containment:

  •        First, it's easier to follow. The contained objects are represented by “real” things explicitly, and they can invoke functions or other behavior. Using inheritance makes the relationship appear more abstract.
  •        Second, inheritance can raise problems, particularly inherits from more than one base class. Because sometimes, two different classes have the same name of methods.
  •        Third, containment allows you to include more than one sub-object of the same class.

However, private inheritance does offer features beyond those provided by containment. For example, a class has protected members, and such members can only available to derived classes. Private inheritance but not containment can deal with this kind of members. 

Another situation that calls for using inheritance is if you want to redefine virtual functions. Again, this is a privilege accorded to a derived-class but not to a containment class.

 

7. Protected Inheritance

Protected inheritance is a variation on private inheritance, like private or public inheritance, it use the keyword protected when listing a base class:

                             class B-Class: protected string

                          {}

With protected inheritance, public and protected members of a base-class become protected members of the derived class. The main difference between private and protected inheritance appears when derived another class from the derived class (we can call it the third generation). With protected inheritance, the public functions of first generation are available to the third generation, while private can't.

 

8. Redefining Access with using

Suppose you want to make a particular base class method availablepublicly in the derived class (like the public inheritance). One option is todefine a derived-class method that use the base-class method, for example:

int size()    //public Student method
{
         return string::size()
}
so that

                             stu.size();  //stu is an Student object

will compute the size (or length) of the string inside the Student object.

       This option seems unnecessary.There is an alternative to wrapping one function call in another: to use a using declaration to announce that a particular base-class member can be used outside the derived-class, even though the derivation is private. The same example, we type:

class Student: private string, private DbArray
{
public:
        using string::size;
        ...
}
can reach the same requirement. Note that the using declaration just uses the member name----no parentheses, no function natures, no return types.

There is an old way to redeclare base-class methods in a privately derived class: placing the method name in the public section of the derived class: 

class Student: privatestring, private DbArray
{
public:
        string::size;
        ...
}
This looks like a using declaration without using keyword. This approach is deprecated, meaning that the intention is to phase it out.




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