私有继承和包含的关系和区别

首先,来看两种类的定义:

这是包含:

class Student
{
private:
	typedef std::valarray<double> ArrayDb;
	std::string name;
	ArrayDb scores;
	std::ostream & arr_out(std::ostream & os)const;
public:
	Student():name("Null Student"),scores(){}
	explicit Student(const std::string & os):name(os),scores(){}
	explicit Student(int n):name("Nully"),scores(n){}
	Student(const std::string &s,int n):name(s),scores(n){}
	Student(const std::string &s,const ArrayDb &a):name(s),scores(a){}
	Student(const char *str,const double *pd,int n):name(str),scores(pd,n){}
	~Student(){}
	double Average()const;
	const std::string &Name()const;
	double &operator[](int i);
	double &operator[](int i)const;

	friend std::istream & operator >>(std::istream &is,Student &stu);
	friend std::istream & getline(std::istream & is,Student & stu);
	friend std::ostream & operator<<(std::ostream & os,const Student & stu);
};

这是私有继承的:

class Student:private std::string,private std::valarray<double>
{
private:
	typedef std::valarray<double> ArrayDb;
	std::ostream & arr_out(std::ostream & os)const;
public:
	Student():std::string("Null Student"),ArrayDb(){}
	explicit Student(const std::string &s):std::string(s),ArrayDb(){}
	explicit Student(int n):std::string("Nully"),ArrayDb(n){}
	Student(const std::string & s, int n):std::string(s),ArrayDb(n){}
	Student(const std::string &s,const ArrayDb & a):std::string(s),ArrayDb(a){}
	Student(const char * str,const double * pd,int n):std::string (str),ArrayDb(pd,n){}
	~Student(){}
	double Average()const;
	double & operator[](int i);
	double & operator[](int i)const;
	const std::string & Name()const;

	friend std::istream & operator>>(std::istream & is,Student & stu);
	friend std::istream & getline(std::istream & is,Student & stu);
	friend std::ostream & operator<<(std::ostream & os,const Student & stu);
	
};
使用私有继承,雷将继承实现。例如,从String类派生出Student类,后者将有一个String类组件,可用于保存字符串。另外,Student方法可以使用String方法来访问string组件。

包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个未被命名的继承对象添加到类中。

因此,私有继承提供的特性与包含相同:获得实现,但不获得接口。所以,私有继承也可以用来实现has-a关系。

在私有继承重新定义Student类中,新的累不需要私有数据,因为两个基类已经提供了所需要的所有的数据成员。包含版本提供了两个被显式命名的对象成员,而私有继承提供了两个无名称的子对象成员。这事两种方法的第一个主要区别。

由于包含显式的命名了数据成员name,因此可以这样定义构造函数:

Student(const std::string &s,const ArrayDb &a):name(s),scores(a){}
但是对于私有继承来说,只能这样来定义:

Student(const std::string & s, int n):std::string(s),ArrayDb(n){}
这是两者的第二个区别。

对于访问基类的方法时,包含是这样定义的:

double Student::Average()const
{
	if(scores.size()>0)
		return scores.sum()/scores.size();
	else
		return 0;
}
而私有继承和它差不多,只不过由于没有了显式定义的数据成员,因此定义如下:

double Student::Average()const
{
	if(ArrayDb::size()>0)
		return ArrayDb::sum()/ArrayDb::size();
	else
		return 0;
}
从上面的例子,可以很明显的看出,使用包含试将使用对象名来调用方法,而使用私有继承将使用类名和作用域解析运算符来调用方法。

对于访问基类对象时:

使用作用域解析运算符可以访问基类的方法,但如果要使用基类对象本身,该如何做呢?例如,Student类的包含版本实现了Name()方法,它返回string对象成员name;但使用私有继承时,该string对象没有名称,那么,Student类的代码如何访问内部string对象呢?

答案是使用强制类型转换。由于Student类是从string类派生而来的,因此可以通过强制转换,将Student对象转换为string对象;结果为继承而来的string对象。我前面介绍过,指针this指向用来调用方法的对象,因此*this为用来调用方法的对象,在这个例子中,为类型为Student的对象。为了避免调用构造函数创建新的对象,可使用强制类型转换来创造一个引用:

<span style="font-family:SimSun;">const string & Student::Name()const
{
<span style="white-space:pre">	</span>return (const string &) *this;
}</span>
上述方法返回一个引用,该引用指向用于调用该方法的Student对象中的继承而来的string对象。

访问基类的友元函数:

用类名显式的限定函数名不适合于友元函数,这事因为友元函数不属于类。然而,可以通过显式地转换为基类来调用正确的函数。例如:

<span style="font-family:SimSun;">ostream & operator<<(ostream & os,const student & stu)
{ 
  os<<"scores for "<<(const string &)stu<<":/n";
  ... 
}</span>
如果plato是一个student对象,则cout<<plato;将调用上述函数,stu将是指向plato的引用,而os将是指向cout的引用。下面的代码:
<span style="font-family:SimSun;font-size:10px;">os<<"scores for "<<(const string &)stu<<":/n";</span>
显示地将stu转换为string对象引用,这与operator<<(ostream &,const string &)函数匹配。

引用stu不会自动的转换为string引用。根本原因在于,在私有继承中,在不进行显示类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针。

(即使这个例子中使用的是公有继承,也必须使用显示类型转换。原因之一是,如果不使用类型转换,代码(so<<stu)将与友元函数原型匹配,从而导致递归调用。另一个原因是,该类使用多重继承,编译器无法确定应该转换成那个基类,如果两个基类都提供了函数operator<<()的话。)

那么,使用包含还是私有继承呢?

对于使用包含来说,它易于理解。类声明中包含表示被包含类的显示命名对象,代码可以通过名称引用这些对象,而使用继承将使关系更抽象。其次,继承会引起许多问题,尤其从多个基类继承时,可能必须处理许多问题,例如包含同名方法的独立的基类,或共享祖先的独立基类。而使用包含则不大可能会遇到这样的麻烦。另外,包含能够包括多个同类的子对象。如果某个类需要3个string对象,可以使用包含声明3个独立的string成员。而继承则只能使用一个这样的对象(当对象都没有名称时,将难以区分)。
    但私有继承所提供的特性却比包含多。例如,假设类包含保护成员(可以是数据成员,也可以是成员函数),则这样的成员在派生类中是可用的,但在继承层次结构外是不可用的。如果使用组合将这样的类包含在另一个类中,则后者将不是派生类,而是位于继承层次结构之外,因此不能访问保护成员。但通过继承得到的将是派生类,因此它能够访问保护成员。
    另一种需要使用私有继承的情况是需要重新定义虚函数。派生类可以重新定义虚函数,但包含类不能。使用私有继承,重新定义的函数将只能在类中使用,而不是公有的。

通常,使用包含来建立has-a关系;如果新类需要访问原有类的保护成员,或需要重新定义虚函数,则应使用私有继承。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值