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

本文深入探讨了C++中私有继承的概念及其与成员包含的区别。解释了如何使用私有继承来实现has-a关系,并对比了其与公共继承的特点。此外,还介绍了如何在私有继承中访问基类的方法和成员。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       前几天装了个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.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值