类和继承(四):面向对象编程

一个面向对象程序范例

  • 面向对象对象编程三要素:
    1. 数据抽象
    1. 继承
    1. 动态绑定

适用于大型程序

以算术表达式树形表示的面向对象解决方案为例

创建和打印表达式

  • 定义表达式树节点类
    • 每个节点可能有一到三个子节点
    • 可用一个联合加上一个指示节点类型的专门字段表示节点
  • 三种节点:

    1. 包含一个整数值,无子节点
    1. 包含一个操作符,一个子节点
    1. 包含一个操作符,两个子节点

当需要用到一个类型字段时可以考虑定义一系列类并用继承组织起来能否更有效

  • 用一个类表示“节点”概念
1
2
3
4
5
6
7
8
9
10
11
12
13
class Expr_node {
// print the expression
friend ostream& operator<<(ostream&, const Expr_node&);
protected:
virtual void print(ostream&) const = 0;
virtual ~Expr_node() {}
};
ostream& operator<<(ostream& o, const Expr_node& e) {
e.print(o);
return o;
}
  • 用继承声明具体类型
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
// save first node-type
class Int_node: public Expr_node {
friend class Expr;
int n;
Int_node(int k): n(k) {}
void print(ostream& o) const { o << n };
};
// save second type
class Unary_node: public Expr_node {
friend class Expr;
string op;
Expr_node* opnd;
Unary_node(const string& a, Expr_node* b): op(a), opnd(b) {}
void print(ostream& o) const {
o << "(" << op << *opnd << ")";
}
};
// save third type
class Binary_node: public Expr_node {
friend class Expr;
string op;
Expr_node* left;
Expr_node* right;
Binary_node(const string& a, Expr_node* b, Expr_node* c):
op(a), left(b), right(c) {}
void print(ostream& o) const {
o << "(" << *left << op << *right << ")";
}
};
  • 用句柄类管理指针避免使用者管理内存的麻烦
    • 使用者关心的只是树和子树而非单个节点,定义句柄类Expr隐藏Expr_node继承层次
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
class Expr {
friend ostream& operator<<(ostream&, const Expr&);
Expr_node* p;
public:
Expr(int);
Expr(const string&, Expr);
Expr(const string&, Expr, Expr);
Expr(const Expr&);
Expr& operator=(const Expr&);
~Expr() { delete p; }
};
Expr::Expr(int n) {
p = new Int_node(n);
}
Expr::Expr(const string& op, Expr t) {
p = new Unary_node(op, t);
}
Expr::Expr(const string& op, Expr left, Expr right) {
p = new Binary_node(op, left, right);
}

改进

  • 可在Expr_node派生层次加入虚函数copy
  • 加入引用计数避免复制下层Expr_node会更有效率
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
79
80
81
82
83
84
85
86
87
class Expr_node {
friend ostream& operator<<(ostream&, const Expr&);
friend class Expr;
// add a use count
int use;
protected:
Expr_node(): use(1) {}
virtual void print(ostream&) const = 0;
virtual ~Expr_node() {}
};
class Expr {
friend ostream& operator<<(ostream&, const Expr&);
Expr_node* p;
public:
Expr(int n) {
p = new Int_node(n);
}
Expr(const string& op, Expr t) {
p = new Unary_node(op, t);
}
Expr(const string& op, Expr left, Expr right) {
p = new Binary_node(op, left, right);
}
Expr(const Expr& t) { p = t.p; ++p->use; }
~Expr() { if (--p->use == 0) delete p; }
Expr& operator=(const Expr&);
};
// assignment operator increase use count of the right object
Expr& Expr::operator=(const Expr& rhs) {
rhs.p->use++;
if (--p->use == 0)
delete p;
p = rhs.p;
return *this;
}
ostream& operator<<(ostream& o, const Expr& t) {
t.p->print(o);
return o;
}
// save first type
class Int_node: public Expr_node {
friend class Expr;
int n;
Int_node(int k): n(k) {}
void print(ostream& o) const { o << n };
};
// save second type
class Unary_node: public Expr_node {
friend class Expr;
string op;
Expr opnd;
Unary_node(const string& a, Expr b): op(a), opnd(b) {}
void print(ostream& o) const {
o << "(" << op << opnd << ")";
}
};
// save third type
class Binary_node: public Expr_node {
friend class Expr;
string op;
Expr left;
Expr right;
Binary_node(const string& a, Expr b, Expr c):
op(a), left(b), right(c) {}
void print(ostream& o) const {
o << "(" << left << op << right << ")";
}
};

扩展新操作:表达式求值

  • 方法和打印表达式相同
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
class Expr_node {
friend ostream& operator<<(ostream&, const Expr&);
friend class Expr;
int use;
protected:
Expr_node(): use(1) {}
virtual void print(ostream&) const = 0;
virtual ~Expr_node() {}
// add a pure virtual function
virtual int eval() const = 0;
};
class Expr {
friend class Expr_node;
friend ostream& operator<<(ostream&, const Expr&);
Expr_node* p;
public:
Expr(int n) {
p = new Int_node(n);
}
Expr(const string& op, Expr t) {
p = new Unary_node(op, t);
}
Expr(const string& op, Expr left, Expr right) {
p = new Binary_node(op, left, right);
}
Expr(const Expr& t) { p = t.p; ++p->use; }
~Expr() { if (--p->use == 0) delete p; }
Expr& operator=(const Expr& t);
// new function: calculate the evaluation
int eval() const { return p->eval(); }
};
// save first type
class Int_node: public Expr_node {
friend class Expr;
int n;
Int_node(int k): n(k) {}
void print(ostream& o) const { o << n };
// new function
int eval() const { return n; }
};
// save second type
class Unary_node: public Expr_node {
friend class Expr;
string op;
Expr opnd;
Unary_node(const string& a, Expr b): op(a), opnd(b) {}
void print(ostream& o) const {
o << "(" << op << opnd << ")";
}
// new function
int eval() const;
};
int Unary_node::eval() const {
// only consider certain operands
if (op == "-")
return -opnd.eval();
throw "error, bad op " + op + " int UnaryNode";
}
// save third type
class Binary_node: public Expr_node {
friend class Expr;
string op;
Expr left;
Expr right;
Binary_node(const string& a, Expr b, Expr c):
op(a), left(b), right(c) {}
void print(ostream& o) const {
o << "(" << left << op << right << ")";
}
// new function
int eval() const;
};
int Binary_node::eval() const {
int op1 = left.eval();
int op2 = right.eval();
if (op == "-") return op1 - op2;
if (op == "+") return op1 + op2;
if (op == "*") return op1 * op2;
if (op == "/" && op2 != 0) return op1 / op2;
throw "error, bad op " + op + "in BinaryNode";
}
  • 类的抽象对算术表达式进行了精确建模
  • 扩展程序以计算表达式所需要增加的代码较少

扩展增加新节点类型

  • 如增加Ternary_node表示三元操作符(?:)
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
class Ternary_node: public Expr_node {
friend class Expr;
string op;
Expr left;
Expr middle;
Expr right;
Ternary_node(const string& a, Expr b, Expr c, Expr d):
op(a), left(b), middle(c), right(d) {}
void print(ostream& o) const {
o << "(" << left << " ? " << middle << " : " << right << ")";
}
// new function
int eval() const;
};
int Ternary_node::eval() const {
if (left.eval())
return middle.eval();
else
return right.eval();
}
// add a constructor for Ternary_node in Expr
class Expr {
friend class Expr_node;
friend ostream& operator<<(ostream&, const Expr&);
Expr_node* p;
public:
Expr(int n) {
p = new Int_node(n);
}
Expr(const string& op, Expr t) {
p = new Unary_node(op, t);
}
Expr(const string& op, Expr left, Expr right) {
p = new Binary_node(op, left, right);
}
Expr(const string* op, Expr left, Expr middle, Expr right) {
p = new Ternary_node(op, left, middle, right);
}
Expr(const Expr& t) { p = t.p; ++p->use; }
~Expr() { if (--p->use == 0) delete p; }
Expr& operator=(const Expr& t);
// new function: calculate the evaluation
int eval() const { return p->eval(); }
};

总结

面向对象编程能简化程序的设计和更新过程:
对下层系统中的对象进行建模
保证修改时耦合度低


《C++沉思录(Cplusplus Thinking)》笔记