C++中的代码重用
C++中的代码重用
valarray类简介
头文件valarray
与vector和array比起来, valarray的功能更加强大。valarray的算术支持更多。
valarray <int> v1 ; // int类型的数组,大小为0
valarray <int> v2(8); // 8个Int元素的数组
valarray <double> v3(10,8); // 8个10
double gra[5] = {1.6,12.6,.9,.09,9.99};
valarray<double> v4(gra, 4); // 4个元素的数组, 取gra数组的前4个元素
C++ 11中也可以使用初始化列表
valarry<int> v5 = {1,2,3};
valarry<int> v5 {1,2,3};
valarray类的一些方法
has-a的关系表示组合
示例:
Student.h文件
#ifndef STUDENT_H_
#define STUDENT_H_
#include <iostream>
#include <string>
#include <valarray>
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(int n):name{"Nully"},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); // 1 word
friend std::istream & getline(std::istream & is, Student & stu); // 1 line
// 输出
friend std::ostream &operator <<(std::ostream & os, const Student & stu);
};
#endif
说明: typedef std::valarray<double> ArrayDb;放在类内的私有部分,表示该可以在Student类的实现中使用它,而不能在Student类外使用。
关于隐式类型转换的1点说明:
#include <iostream>
using std::cout;
using std::endl;
class Test
{
private:
int int_num;
public:
Test():int_num(0)
{
cout << "default constructor" << endl;
};
Test(int n):int_num(n)
{
cout << "1 param constructor" << endl;
}
~Test()
{
cout << "destroy test obj..." << endl;
};
Test(const Test &t)
{
cout << "copy constructor" << endl;
int_num = t.int_num;
}
int getNum() const
{
return int_num ;
}
};
int main(int argc, char const *argv[])
{
Test test(10);
cout<< "Output int_num value:" << test.getNum() << endl;
test = 11; // 调用构造函数Test(11)生成零时的Test对象, 使用临时对象来替换原来的test
return 0;
}
一个参数的构造函数,没有使用explicit修饰
Test(int n):int_num(n)
{
cout << "1 param constructor" << endl;
}
test中的值变成临时对象中的值,test地址没变(实验论证,并没有调用拷贝构造函数)
Test test(10);
cout<< "Output int_num value:" << test.getNum() << endl;
test = 11; // 调用构造函数Test(11)生成零时的Test对象, 使用临时对象来替换原来的test
对于继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数。 对于成员对象,构造函数则使用成员名。
Student(const char * str, const double * pd, int n):name(str), scores(pd,n){};
初始化列表中每一项都调用与之匹配的构造函数。即name(str)调用构造函数string(const char *), scores(pd,n)调用构造函数ArrayDb(const double * , int);
C++ 要求在构建对象其他部分之前, 先构建继承对象的所有成员对象, 如果省略初始化列表, C++将使用成员对象所属类的默认构造函数。
初始化顺序:初始化列表中包含多个项目时吗, 初始化顺序为成员被声明的顺序。而不是在初始化列表中的顺序。
Student.cpp文件
#include "student.h"
using std::endl;
using std::istream;
using std::ostream;
using std::string;
//
double Student::average() const
{
if(scores.size() > 0)
{
return scores.sum() / scores.size();
}else
return 0;
}
inline const std::string & Student::Name() const
{
return name;
}
inline double & Student::operator[](int i)
{
return scores[i];
}
inline double Student::operator[](int i) const
{
return scores[i];
}
// 私有方法
std::ostream & Student::arr_out(std::ostream & os) const
{
int i ;
int lim = scores.size();
if(lim > 0)
{
for(i= 0; i< lim ; i++)
{
os << scores[i] << " " ;
if(i % 5 != 0)
{
os << endl;
}
}
}
else
{
os << " empty array" ;
}
}
// friends
std::istream & operator>> (std::istream &is, Student & stu)
{
is >> stu.name;
return is;
}
std::istream & getline(std::istream & is, Student & stu)
{
getline(is,stu.name);
return is;
}
std::ostream &operator <<(std::ostream & os, const Student & stu)
{
os <<"Scores for " << stu.name << endl;
stu.arr_out(os);
return os;
}
use_stu.cpp文件
#include "student.h"
// #include <iostream>
using std::cout;
using std::endl;
using std::cin;
void set(Student & sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main(int argc, char const *argv[])
{
Student ada[pupils] = {Student(quizzes),Student(quizzes),Student(quizzes)};
int i ;
for(i = 0; i< pupils; i++)
{
set(ada[i], quizzes);
}
cout << "\n Students List: \n";
for(i = 0; i < pupils; i++)
{
cout << endl << ada[i];
cout << "average: " << ada[i].average() << endl;
}
cout << "Done!" ;
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];
}
while(cin.get() != '\n')
{
continue;
}
}
私有继承
包含(组合)是将对象作为一个命名的成员对象添加到类中,而私有继承(private inheritance)将对象作为一个未命名的继承对象添加到类中。
class Student :private std::string, private std::valarray<double>
{
}// 私有继承提供了两个无名称的子对象成员
初始化问题:
对于继承类, 新版本的构造函数将使用成员初始化列表语法, 它使用类名而不是成员名来标识构造函数:
Student(const char *str, const double *pd, int n)
:std::string(str),ArrayDb(pd,n)
student1.h头文件
#ifndef STUDENT1_H_
#define STUDENT1_H_
#include <iostream>
#include <string>
#include <valarray>
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 double *pd,int n): std::string(s), 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);
};
#endif
访问基类的方法:
使用私有继承时,只能在子类的方法中使用父类的方法。
使用组合时将使用对象名来调用方法, 而使用私有继承时将使用类名和作用域解析运算符来调用方法。
使用私有继承时,子类没有父类对象的名称, 那么如何访问父类对象呢?
答: 进行强制类型转换。
const string & Student::Name() const
{
return (const string &) *this;
}
访问基类的友元函数
友元函数不属于类成员。 然而,可以通过显式地转换为基类来调用正确的函数。
ostream & operator<<(ostream & os , Student & stu)
{
os << "Scores for " << (const string &)stu << ": \n" ;
}
在私有继承中,未进行显式类型转换的派生类引用或者指针, 是无法赋值给基类的引用或指针的。原因1: 如果不转换, 将进入无限的递归调用; 原因2,如果不转换, 因为类是调用的多重继承, 无法指定调用哪个基类的方法。
私有继承中必须使用基类的类名作为作用域来访问基类中的方法 base::method();student1.cpp
#include "student1.h"
using std::endl;
using std::istream;
using std::ostream;
using std::string;
double Student::average() const
{
if (ArrayDb::size() > 0)
{
return ArrayDb::sum() / ArrayDb::size();
}
else
return 0;
}
double &Student::operator[](int i)
{
return ArrayDb::operator[](i);
}
double Student::operator[](int i) const
{
return ArrayDb::operator[](i);
}
const std::string &Student::name() const
{
return (const string &)*this;
}
std::ostream &Student::arr_out(std::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!" << endl;
return os;
}
std::istream &operator>>(std::istream &is, Student &stu)
{
is >> (string &)stu;
return is;
}
std::ostream &operator<<(std::ostream &os, const Student &stu)
{
os << "Scores for" << (const string &)stu;
stu.arr_out(os);
return os;
}
std::istream & getline (std::istream &is, Student & stu)
{
getline(is,(string &)stu);
return is;
}
main文件:
#include "student1.h"
// #include <iostream>
using std::cout;
using std::endl;
using std::cin;
void set(Student & sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main(int argc, char const *argv[])
{
Student ada[pupils] = {Student(quizzes),Student(quizzes),Student(quizzes)};
int i ;
for(i = 0; i< pupils; i++)
{
set(ada[i], quizzes);
}
cout << "\n Students List: \n";
for(i = 0; i< pupils ;i++)
{
cout << ada[i].name() << endl;
}
for(i = 0; i < pupils; i++)
{
cout << endl<< ada[i];
cout << "average: " << ada[i].average() << endl;
}
cout << "Done!" ;
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];
}
while(cin.get() != '\n')
{
continue;
}
}
上述代码的一点说明:
double & operator[](int i); // 1
double operator[](int i) const; // 2
这两个语句 咋一看是不能重载的, 而在实际操作过程中是可以发生重载的。比如说,
Student s1(3);
s1[0]; // 调用 第1条语句
const Student s2(3);
s2[0]; // 调用语句 2
说明const 也能作为函数重载的调用规则。
组合和私有继承的区别:
- 是否显式使用成员
- 列表初始化
- 调用方法时是否添加作用域
使用包含还是继承?
优先考虑组合。
使用私有继承的情况:
- 相应访问 某个类的protected成员。 使用组合时访问不了的, 但使用私有继承可以直接访问。
- 重新定义虚函数。 派生类可以重新定义虚函数。 使用私有继承的话, 重新定义的虚函数只能在类中使用, 而不是公有的。
总结: 通常,应该使用组合来建立has-a关系, 如果新类需要访问原有类的保护成员,或者需要重新定义虚函数,则应该使用私有继承。
使用保护继承
class Student :protected std::string, protected std::valarray<double>
{
}
使用保护继承时,父类的公有成员和保护成员都会成为子类的保护成员。
隐式向上转换(implicit upcasting)意味着无需进行显式类型转换, 就可以将基类指针或引用指向派生类对象。
各种继承方式
| 特征 | 公有(public)继承 | 保护(protected)继承 | 私有(private)继承 |
|---|---|---|---|
| 公有成员变成 | 派生类的公有成员 | 派生类的保护成员 | 派生类的私有成员 |
| 保护成员变成 | 派生类的保护成员 | 派生类的保护成员 | 派生类的私有成员 |
| 私有成员变成 | 只能通过基类接口访问 | 只能通过基类的接口访问 | 只能通过基类的接口访问 |
| 能否向上隐式转换 | 是 | 是(但只能在派生类中) | 否 |
使用using重新定义访问权限
方法1:定义一个公有函数,包含一个私有函数。
方法2:在子类的公有部分使用using 声明,声明要使用的父类成员。
class Student :protected std::string, protected std::valarray<double>
{
public:
using std::valarray<double>::min;
using std::valarray<double>::max;
}
上述声明使得valarray
using声明只适用于继承,而不适用于包含。
浙公网安备 33010602011771号