您的位置:首页 > 编程语言 > C语言/C++

《C++沉思录》——面向对象

2016-06-04 18:33 423 查看
面向对象——数据抽象、继承、动态绑定(规模大、方便修改),下面是面向对象和句柄类综合应用案例:

问题描述:

算术表达式树,(-5)*(3+4)



样例展示说明:

void main()
{
Expr expression = Expr("*", Expr("-", "5"), Expr("+", "3", "4"));
cout << expression << endl;
expression = Expr("*", expression, expression);
cout << expression << endl;
}
输出:
((-5)*(3 + 4))
((-5)*(3 + 4))*((-5)*(3 + 4))


分析:

树的结构分为三种结点:(1)整数表达式,无子节点;(2)一元表达式,有一个操作符和一个子节点;(3)二元表达式,有一个操作符和两个子节点。

第一种思路:

因为我们不知道子节点类型,所以构造不同类型的节点时我们绝不能按值,而是传入父类指针。

class Expr_node
{
//main中的输出示例采用<<输出操作符来打印
friend ostream& operator<<(ostream&, const Expr_node&);
protected:
//输出不同类型的节点,需要使用动态绑定来决定打印方式
virtual void printNode(ostream&) const = 0;
virtual ~Expr_node() { };
};

ostream& operator<<(ostream& out, const Expr_node& node)
{
node.printNode(out);
return out;
}

class Int_Node :public Expr_node
{
friend class Expr;
private:
int n;
Int_Node(int k) : n(k) { }
void printNode(ostream& out) const
{
out << n;
}
};

class Unary_node :public Expr_node
{
friend class Expr;
private:
string op;
Expr_node* opnd;  //因为我们不知道子节点类型,所以构造不同类型的节点时我们绝不能按值,而是传入父类指针。
Unary_node(const string& a, Expr_node* b) :op(a), opnd(b) { }
void printNode(ostream& out) const
{
out << "(" << op.c_str() << opnd << ")";
}
};

class Binary_Node :public Expr_node
{
friend class Expr;
private:
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 printNode(ostream& out) const
{
out << "(" << left << op.c_str() << right << ")";
}
};
(1)我们先不说题目要求的调用和创建的树的形式,就按上面的方法来动态分配创建节点:

//一层一层创建,以便内存分配和回收
Int_Node* iNode1 = new Int_Node(5);
Int_Node* iNode2 = new Int_Node(3);
Int_Node* iNode3 = new Int_Node(4);

Unary_node* expr1 = new Unary_node("-", iNode1);
Binary_Node* expr2 = new Binary_Node("+", iNode2, iNode3);

Binary_Node* t = new Binary_Node("*", expr1, expr2);

...

delete ...
       假设我们也可以有耐心有超强记忆力能伤处创建的每个对象。(毕竟这是愚蠢和有难度的 )

(2)如果你说我们可以在Unary_node和Binary_Node的析构函数中去处理内存的释放,那么如果析构函数删除了操作数,可能会多次删除对象,因为可能不止一个Expr_Node指向同一个下层的表达式对象。

       所以行不通,很麻烦。

第二种思路:

引入句柄类Expr类,所有的Expr_Node的继承层次都包含在Expr,而用户不再能看到Expr_Node对象。

#include<iostream>
using namespace std;

class Expr_node
{
friend class Expr;    //Expr需要帮助Expr_node管理引用计数,两者协同管理引用计数,仅当引用计数为0时才删除该节点

public:
//引用计数初始为1
Expr_node() :useCount(1) { }
//输出不同类型的节点,需要使用动态绑定来决定打印方式
virtual void printNode(ostream&) const = 0;
virtual ~Expr_node() { };

virtual int eval() const = 0;

private:
//引入引用计数,避免对下层Expr_node的复制(反正也不改变它,没必要复制)
//引用计数————指明同时有多少个Expr指向同一个Expr_node
int useCount;
};

class Expr
{
friend std::ostream& operator<<(std::ostream& out, const Expr&);
public:
Expr(int);                         //创建Int_Node节点
Expr(const string&, Expr);         //创建Unary_Node节点
Expr(const string&, Expr, Expr);   //创建Binary_Node节点
Expr(const string&, Expr, Expr, Expr);  //创建Ternary_Node节点
Expr(const Expr&);
Expr& operator=(const Expr&);
~Expr();

int eval() const
{
return pNode->eval();
}

private:
Expr_node* pNode;      //构造函数将创建合适类型的Expr_node的地址存储在pNode中,析构函数负责释放构造函数动态分配的节点
};

class Int_Node :public Expr_node    //整数表达式
{
friend class Expr;
private:
int n;
Int_Node(int k) : n(k) { }
void printNode(ostream& out) const
{
out << n;
}
int eval() const
{
return n;
}
};

class Unary_node :public Expr_node   //一元表达式
{
friend class Expr;
private:
string op;
Expr opnd;  //因为我们不知道子节点类型,所以构造不同类型的节点时我们绝不能按值,而是传入父类指针。
Unary_node(const string& a, Expr b) :op(a), opnd(b) { }
void printNode(ostream& out) const
{
out << "(" << op.c_str() << opnd << ")";
}
int eval() const
{
if (op == "-")
return -opnd.eval();
}
};

class Binary_Node :public Expr_node   //二元表达式
{
friend class Expr;
private:
string op;
Expr left;  //因为我们不知道子节点类型,所以构造不同类型的节点时我们绝不能按值,而是传入父类指针。
Expr right;
Binary_Node(const string& a, Expr b, Expr c) :op(a), left(b), right(c) { }
void printNode(ostream& out) const
{
out << "(" << left << op.c_str() << right << ")";
}
int 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;
}
}
};

class Ternary_Node :public Expr_node  // 三元表达式
{
friend class Expr;
private:
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 printNode(ostream& out) const
{
out << "(" << left << " ? " << middle << " : " << right << ")";
}
int eval() const
{
if (left.eval())
return middle.eval();
else
return right.eval();
}
};

//针对Expr的输出操作符
std::ostream& operator<<(std::ostream& out, const Expr& t)
{
t.pNode->printNode(out);
return out;
}

Expr::Expr(int n)
{
pNode = new Int_Node(n);
}

Expr::Expr(const string& op, Expr n)
{
pNode = new Unary_node(op, n);
}

Expr::Expr(const string& op, Expr a, Expr b)
{
pNode = new Binary_Node(op, a, b);
}

Expr::Expr(const string& op, Expr left, Expr middle, Expr right)
{
pNode = new Ternary_Node(op, left, middle, right);
}

Expr::Expr(const Expr& t)    //拷贝构造,两者指向是同一个Expr_node
{
pNode = t.pNode;
++pNode->useCount;
}

Expr& Expr::operator=(const Expr& t)   //赋值运算符,需要递增右边、递减左边
{
t.pNode->useCount++;

if (--pNode->useCount == 0)
delete pNode;

pNode = t.pNode;

return *this;
}

Expr::~Expr()
{
if (--pNode->useCount == 0)
{
delete pNode;
}
}

void main()
{
Expr expression = Expr("*", Expr("-", 5), Expr("+", 3, 4));
cout << expression << "=" << expression.eval() << endl;
expression = Expr("*", expression, expression);
cout << expression << "=" << expression.eval() << endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息