c++中的代码重用-----14.1 包含对象成员的类

文章目录

    • 14.1 包含对象成员的类
        • valarray类模板
      • Student类的设计————使用对象组合(包含)的方式设计类
        • 接口和实现
        • 1. 初始化被包含的对象
        • 2. 使用被包含对象的接口

14.1 包含对象成员的类

c++促进代码重用的方式:

  • 公有继承
  • 包含对象(对象组合、或叫层次化)
  • 私有继承
  • 保护继承

其中公有继承用于实现is-a关系,
而包含、私有继承和公有继承用于实现has-a关系(即新的类包含另一个类的对象)。

valarray类模板

该类模板由头文件"valarray"支持.
模板特性意味着声明对象时,必须指定具体的数据类型

学习使用类模板valarray,必须了解这个类的构造函数及其他类方法。
valarray的使用方法举例:

double gpa[5] = {3.1, 3.5, 2.9, 8.7, 4.6};
valarray<double> v1;  // double型数组,size = 0 
valarray<int> v2(8); // int型数组,size = 8
valarray<int> v3(10,8); // 8个int数组,每个元素设置值为10
valarray<int> v4(gpa,4); //size = 4, val = gpa ,type = int
一些方法:
operator[]() :[]访问各个元素
size() : 返回包含的元素数
sum()
max()
min() 

Student类的设计————使用对象组合(包含)的方式设计类

class Student
{   
private:
    // 将该typedef放在类定义的私有部分意味着可以在Student类的实现中使用它
    // 但在student类外面不能使用
    typedef std::valarray<double> ArrayDb;
    std::string name;       // contained object
    ArrayDb scores;         // contained object
    ......
}

学生不是姓名,也不是分数,而是学生有姓名和分数,所以这里是has-a关系,所以这里使用组合的方式来建立has-a关系。

接口和实现

上述将数据成员设定为私有的,这意味Student类的成员函数可以使用string类和 valarray类的公有接口来访问和修改name对象和score对象
但是在类外访问不到成员类对象的接口和数据,只能通过Student类提供的接口对私有的成员类进行访问。

也就是说:

  • 对象组合的方式:Student类对象获得了成员类的实现,但没有继承其接口
  • 而公有继承的方式:类对象继承了接口,可能还有实现(基类的虚函数提供了接口,但是没有提供实现)。

类对象不能自动获得被包含的成员对象的接口,但是可以在类方法的实现中使用成员对象的接口来修改成员对象。

以下所有的构造函数都是内联的,还提供了一些用于输入输出的友元函数。

class Student
{   
private:
    typedef std::valarray<double> ArrayDb;
    std::string name;       
    ArrayDb scores;         
    // private method for scores output
    std::ostream & arr_out(std::ostream & os) const;
public:
    // dafault construction
    Student() : name("Null Student"), scores() {}
    explicit Student(const std::string & s)
        : name(s), scores() {}
    // 这里的参数int n是指数组元素的个数,不是数组中的值
    // 而一个参数的构造函数有可能会被用于隐式类型转换,发生不必要的错误。 // 使用关键字explicit关闭隐式转换,避免发生未经允许的隐式类型转换
    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;
// friends
    // input
    friend std::istream & operator>>(std::istream & is,
                                     Student & stu);  // 1 word
    friend std::istream & getline(std::istream & is,
                                  Student & stu);     // 1 line
    // output
    friend std::ostream & operator<<(std::ostream & os,
                                     const Student & stu);
};

需要注意的点:

  1. typedef std::valarray ArrayDb;将typedef放在私有部分说明只可以在类内使用,类外不能使用。
  2. explicit Student(const std::string & s): name(s), scores() {}
    explicit Student(int n) : name(“Nully”), scores(n) {}
    使用关键字explicit关闭隐式类型转换,防止一个参数的构造函数被用作转换函数,在不知情的情况下进行错误的类型转换。

1. 初始化被包含的对象

  • 成员初始化列表可以用于初始化内置类型的的成员
  • 使用成员初始化列表初始化派生类的基类部分,使用类名,调用特定的构造函数进行初始化
  • 对于成员对象,构造函数则使用成员名(与内置类型的方式保持一致),调用相应类的构造函数进行初始化
  • 成员初始化顺序与初始化列表的排列顺序无关,而是与类内声明顺序相同。

C++要求在构建对象的其他部分之前,先构建继承对象的所有成员的所有成员对象;因此如果省略初始化列表,C++将使用成员对象所属类的默认构造函数。

2. 使用被包含对象的接口

  • 被包含的成员对象的接口不是公有的,但是可以使用在类方法中使用它。

例1:

//public methods
// 被包含的对象的接口不是公有的,但是可以在类方法中使用他们,但是不在类外使用
// 该方法内部使用了valarray的方法size和sum 
double Student::Average() const
{
    if (scores.size() > 0)
        return scores.sum()/scores.size();  
    else
        return 0;
}

例2
这里的stu.name是一个string对象,os << string ,将调用string类的 <<运算符重载函数,进行输出。

// use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
    // 这里使用stu.name将调用operator<<(ostream &)
    os << "Scores for " << stu.name << ":\n";
    stu.arr_out(os);  // use private method for scores
    return os;
}

你可能感兴趣的:(c++,primer,plus学习笔记,c++,开发语言)