私有继承
公有继承实现is-a模型,私有继承实现has-a模型.
私有继承将基类的公有成员和保护成员继承成为派生类的私有成员,这意味着派生类对象不能使用私有函数,但是在派生类域内也能使用私有成员.
包含将对象作为一个命名的成员对象添加到类中,私有继承将对象作为一个未被命名的继承对象添加到类中,称为子对象.这是和包含的第一个区别.
这样私有继承就完成了has-a模型的实现.
私有继承和包含的区别
私有继承格式如下:
class Student : private string,private valarray<double>
{
public:
...
}
由于使用子对象来描述私有对象继承的对象,子对象是没有名字的,之前包含采用初始化列表初始化对象:
Student(const char * str, const double * pd, int n)
: name(str), scores(pd, n) {}
由于私有继承没有对象名字,所以类名隐式调用构造函数:
Student(const char * str, const double * pd, int n)
: string(str), ArrayDb(pd, n) {}
这是和包含的第二个区别.
利用私有继承实现Student类
类声明如下:
class Student : string, valarray<double> // 采用了多重继承,这个例子不会发生冲突
{
private:
typedef valarray<double> ArrayDb;
// 私有的输出函数
std::ostream & arr_out(std::ostream & os) const;
public:
Student() : string("Null Student"), ArrayDb() {}
/ ***************************** /
* 由于采用了私有继承,继承了没 *
* 有名字的对象,直接用类名进行 *
* 隐式初始化 *
/ ***************************** /
explicit Student(string & s)
: string(s), ArrayDb() {}
explicit Student(int n) : string("Nully"), ArrayDb(n) {}
Student(string & s, int n)
: string(s), ArrayDb(n) {}
Student(string & s, const ArrayDb & a)
: string(s), ArrayDb(a) {}
Student(const char * str, const double * pd, int n)
: 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); // 1 word
friend std::istream & getline(std::istream & is,
Student & stu); // 1 line
// 友元输出函数
friend std::ostream & operator<<(std::ostream & os,
const Student & stu);
};
通过域解析运算符来解决访问基类函数的方法:
包含所用的方法:
double Student::Average() const
{
if (ArrayDb::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类是从string类派生而来的,可以使用枪支转换,转换为基类对象,为了避免创造新的对象,所以,强制转换为const string&引用类型,如下:
const string & Student::Name() const
{
return (const string &) *this; // *this代表调用函数的对象
}
通过类名和作用域解析运算符调用基类的友元函数不合适,因为友元函数不属于类函数,然而,可以显式转换为基类,在正确调用基类的友元函数.
ostream & operator<<(ostream & os, const Student & stu)
{
os << "Scores for " << (const string &) stu << ":\n";
stu.arr_out(os); // use private method for scores
return os;
}
将Student对象转换为基类string的对象,通过调用基类的operator<<(ostream & , const string &).
私有继承中,没有进行显式的强制转换的派生类和指针,无法赋值给基类引用和指针.
就算是公有继承,也必须是显示转换,因为os<<stu与自己匹配,将递归调用自己.
Student类函数如下:
double Student::Average() const
{
if (ArrayDb::size() > 0)
return ArrayDb::sum()/ArrayDb::size();
else
return 0;
}
const string & Student::Name() const
{
return (const string &) *this;
}
double & Student::operator[](int i)
{
return ArrayDb::operator[](i); // 使用ArrayDb::operator[]()类函数
}
double Student::operator[](int i) const
{
return ArrayDb::operator[](i); // 同上
}
// 私有函数,通过循环打印每一个数组元素
ostream & Student::arr_out(ostream & os) const
{
int i;
int lim = ArrayDb::size();
if (lim > 0)
{
for (i = 0; i < lim; i++)
{
os << ArrayDb::operator[](i) << " ";
if (i % 5 == 4)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << " empty array ";
return os;
}
// 友元输入函数,输入姓名
istream & operator>>(istream & is, Student & stu)
{
is >> (string &)stu;
return is;
}
// 使用string友元函数getline(ostream &, const string &)
istream & getline(istream & is, Student & stu)
{
getline(is, (string &)stu);
return is;
}
// 有元输出姓名和成绩
ostream & operator<<(ostream & os, const Student & stu)
{
os << "Scores for " << (const string &) stu << ":\n";
stu.arr_out(os); // use private method for scores
return os;
}
测试函数如下:
void set(Student & sa, int n); // 声明一个输入类姓名和成绩的函数
const int pupils = 3; // Student对象个数
const int quizzes = 5; // 有多少门成绩
int main()
{
Student ada[pupils] =
{Student(quizzes), Student(quizzes), Student(quizzes)};
int i;
for (i = 0; i < pupils; ++i) // 给三个Student对象赋值
set(ada[i], quizzes);
cout << "\nStudent List:\n";
for (i = 0; i < pupils; ++i) // 打印出三个Student类的名字
cout << ada[i].Name() << endl;
cout << "\nResults:";
for (i = 0; i < pupils; ++i) // 完整打印出每个对象的成绩和平均成绩
{
cout << endl << ada[i];
cout << "average: " << ada[i].Average() << endl;
}
cout << "Done.\n";
// cin.get();
return 0;
}
void set(Student & sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa);
cout << "Please enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
cin >> sa[i]; // 调用double& operator[](int i)函数,可以作为输入值
while (cin.get() != '\n')
continue;
}
结果如下: