CPP-13-代码重用

前一章节说明的是公有继承,其实还有 包含私有继承保护继承,它们实现了 has-a 关系,即新的类将包含另一个类的对象

包含

首先了解一下valarray类,是由头文件valarray支持的。这个类用于处理 数值(或具有类似特性的类),支持数组等操作。valarray被定义为一个模板类,以便能够处理不同的数据类型。

模板特性意味着声明对象时,必须指定具体的数据类型。因此,使用valarray类来声明一个对象时,需要在标识符valarray后面加上一对尖括号,并在其中包含所需的数据类型

1
2
3
4
5
6
7
valarray<int> q_values;		//an array of int
valarray<double> weight; //an array of double, size 0
valarray<int> v2(8); //an array of 8 int element
valarray<int> v3(10, 8); //an array of 8 int elements, each set to 10

double gpa[5] = {3.1, 3.5, 3.8, 2.9, 3.3};
valarray<double> v4(gpa, 4); //an array of 4 elements initialized to the first 4 elements of gpa

首先声明一个类的定义 studentc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// studentc.h -- defining a Student class using containment
#ifndef STUDENTC_H_
#define STUDENTC_H_

#include <iostream>
#include <string>
#include <valarray>
using namespace std;
class Student
{
private:
typedef valarray<double> ArrayDb;
string name; // contained object
ArrayDb scores; // contained object
// private method for scores output
ostream & arr_out(ostream & os) const;
public:
Student() : name("Null Student"), scores() {}
explicit Student(const string & s) : name(s), scores() {}
explicit Student(int n) : name("Nully"), scores(n) {}
Student(const string & s, int n) : name(s), scores(n) {}
Student(const 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 string & Name() const;
double & operator[](int i);
double operator[](int i) const;
// friends
// input
friend istream & operator>>(istream & is, Student & stu); // 1 word
friend istream & getline(istream & is, Student & stu); // 1 line
// output
friend ostream & operator<<(ostream & os, const Student & stu);
};

#endif

这里用到了 explicit,它的作用是:用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换

  • explicit 关键字只能用于类内部的构造函数声明上
  • explicit 关键字作用于单个参数的构造函数

其次,stringvalarray都算是被包含的类,被包含对象的接口不是公有的,但可以在类方法中使用它(这个好理解,就是 stringvalarray 的函数执行范围在 student 内部一样)

私有继承

使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。而使用公有继承,基类的公有方法将成为派生类的公有方法,基类使用 private 修饰

1
2
3
4
5
class Student : private string, private valarray<double>
{
public:
...
};

使用私有继承时,只能在派生类的方法中使用基类的方法。但有时候可能希望基类工具是公有的。可以使用对象来调用方法也能 使用 类名和作用域解析运算符 来调用方法

1
2
3
4
5
6
7
8
9
10
11
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;
}

如果要使用基类对象本身呢?可以通过强制转换,因为继承类就是从基类派生出来的,所以可以强制转换

1
2
3
4
const string & Student::Name() const
{
return (const string &) *this;
}

这里指向用于调用该方法的 Student 对象中的继承而来的 string 对象

如果要访问基类的友元函数呢?用类名显式地限定函数名不适合于友元函数,这是因为友元不属于类。然而,可以通过显式地转换为基类来调用正确的函数

1
2
3
4
ostream & operator<< (ostream & os, const Student & stu)
{
os << "Scores for " << (const String &) stu << ":\n";
}

在私有继承中,在不进行显式类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针

保护继承是私有继承的变体。保护继承在列出基类时使用关键字 protected:,各继承方式的权限转换如下

特征 公有继承 保护继承 私有继承
公有成员变成 派生类的公有成员 派生类的保护成员 派生类的私有成员
保护成员变成 派生类的保护成员 生类的保护成员 派生类的私有成员
私有成员变成 只能通过基类接口访问 只能通过基类接口访问 只能通过基类接口访问
能否隐式向上转换 是(但只能在派生类中)

类模板

C++的类模板为生成通用的类声明提供方法

比如我们有一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Item items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty();
bool isfull();
bool push(const Item & item); // add item to stack
bool pop(Tiem & item); // pop top into item
};
#endif

采用模板时,将使用模板定义替换Stack声明,使用模板成员函数替换Stack的成员函数。和模板函数一样,模板类以下面这样的代码开头:

1
template <class Type>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// stacktp.h -- a stack template
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Type items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & item); // add item to stack
bool pop(Type & item); // pop top into item
};

template <class Type>
Stack<Type>::Stack()
{
top = 0;
}

template <class Type>
bool Stack<Type>::isempty()
{
return top == 0;
}

template <class Type>
bool Stack<Type>::isfull()
{
return top == MAX;
}

template <class Type>
bool Stack<Type>::push(const Type & item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}

template <class Type>
bool Stack<Type>::pop(Type & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}

#endif

使用模板类则需要实例化

1
2
Stack<int> kernels;
Stack<string> colonels;

使用模板时主要注意,注意指针可能会失败,因为仅仅创建指针但是并没有分配内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// stcktp1.h -- modified Stack template
#ifndef STCKTP1_H_
#define STCKTP1_H_

template <class Type>
class Stack
{
private:
enum {SIZE = 10}; // default size
int stacksize;
Type * items; // holds stack items
int top; // index for top stack item
public:
explicit Stack(int ss = SIZE);
Stack(const Stack & st);
~Stack() { delete [] items; }
bool isempty() { return top == 0; }
bool isfull() { return top == stacksize; }
bool push(const Type & item); // add item to stack
bool pop(Type & item); // pop top into item
Stack & operator=(const Stack & st);
};

template <class Type>
Stack<Type>::Stack(int ss) : stacksize(ss), top(0) //分配空间
{
items = new Type [stacksize];
}

template <class Type>
Stack<Type>::Stack(const Stack & st) //分配空间
{
stacksize = st.stacksize;
top = st.top;
items = new Type [stacksize];
for (int i = 0; i < top; i++)
items[i] = st.items[i];
}

template <class Type>
bool Stack<Type>::push(const Type & item)
{
if (top < stacksize)
{
items[top++] = item;
return true;
}
else
return false;
}

template <class Type>
bool Stack<Type>::pop(Type & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}

template <class Type>
Stack<Type> & Stack<Type>::operator=(const Stack<Type> & st)
{
if (this == &st)
return *this;
delete [] items;
stacksize = st.stacksize;
top = st.top;
items = new Type [stacksize];
for (int i = 0; i < top; i++)
items[i] = st.items[i];
return *this;
}

#endif

使用逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <cstdlib> // for rand(), srand()
#include <ctime> // for time()
#include "stcktp1.h"
using namespace std;
const int Num = 10;
int main()
{
srand(time(0)); // randomize rand()
cout << "Please enter stack size: ";
int stacksize;
cin >> stacksize;
// create an empty stack with stacksize slots
Stack<const char *> st(stacksize);

// in basket
const char * in[Num] = {
" 1: Hank Gilgamesh", " 2: Kiki Ishtar",
" 3: Betty Rocker", " 4: Ian Flagranti",
" 5: Wolfgang Kibble", " 6: Portia Koop",
" 7: Joy Almondo", " 8: Xaverie Paprika",
" 9: Juan Moore", "10: Misha Mache"
};
// out basket
const char * out[Num];

int processed = 0;
int nextin = 0;
while (processed < Num)
{
if (st.isempty())
st.push(in[nextin++]);
else if (st.isfull())
st.pop(out[processed++]);
else if (rand() % 2 && nextin < Num) // 50-50 chance
st.push(in[nextin++]);
else
st.pop(out[processed++]);
}
for (int i = 0; i < Num; i++)
cout << out[i] << endl;

cout << "Bye\n";
return 0;
}

参考链接

  1. 《C++ Primer Plus》