C ++ meditation Reading Notes (Chapter 8)-an object-oriented program example

Source: Internet
Author: User

 

This example shows three elements of object-oriented programming: data abstraction, inheritance, and dynamic binding.

Problem: The Expression Tree of an arithmetic expression, such as (-5) * (3 + 4), corresponds to the following expression tree:

 

We hope to create such a tree by calling appropriate functions, and then print the complete brackets of the tree. For example:

Expr t = Expr ("*", Expr ("-", 5), Expr ("+", 3, 4 ));

Cout <t <endl;

Output result: (-5) * (3 + 4 ))

 

Object-oriented solutions:

How do I represent a node?

After careful observation, three types of nodes can be abstracted based on the number of subnodes, namely, 1 and 2 subnodes. If I design the node, I will probably describe the node as follows:

 

Class Node

{

Private:

String value; // node value

Int num; // Number of subnodes

Node * son [2]; // store the sub-Node pointer

Public:

Node ();

Node (string &, int); // 0 subnodes

Node (string &, int, Node *); // 1 subnode

Node (string &, int, Node *, Node *); // two subnodes

~ Node ();

};

 

In this way, you need to set a special field class to indicate the number of subnodes. However, this problem is solved by inheritance in the book, which is a great deal!

(1) consider the similarities and differences between the three node classes: both have node values and print nodes, but different node values have different types. Different print nodes have different ways and different number of subnodes.

(2) consider the relationship between the three nodes: each node is independent from other nodes. A node without a subnode is not a node with a subnode, and vice versa.

(3) define an abstract base class based on the above two points. dynamic binding is useful here.

The specific definitions of several node classes are as follows:

 

# Include <iostream>

# Include <string>

Using namespace std;

Class Expr_node

{

Friend ostream & operator <(ostream &, const Expr_node &);

Protected:

Virtual void print (ostream &) const = 0; // dynamic binding

Virtual ~ Expr_node (){}

};

 

Ostream & operator <(ostream & o, const Expr_node & e)

{

E. print (o );

Return o;

}

 

Class Int_node: public Expr_node // 0 subnodes

{

Private:

Int n;

Friend ostream & operator <(ostream &, const Expr_node &);

Public:

Int_node (int k): n (k ){}

Void print (ostream & o) const {o <n ;}

};

 

Class Unary_node: public Expr_node // 1 subnode

{

Private:

String op;

Expr_node * opnd;

Friend ostream & operator <(ostream &, const Expr_node &);

Public:

Unary_node (const string & a, Expr_node * B): op (a), opnd (B ){}

Void print (ostream & o) const {o <"(" <op <* opnd <")";}

};

 

Class Binary_node: public Expr_node // 2 subnodes

{

Private:

String op;

Expr_node * left;

Expr_node * right;

Friend ostream & operator <(ostream &, const Expr_node &);

Public:

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 <")";}

};

 

Void main ()

{

Binary_node * t = new Binary_node ("*",

New Unary_node ("-", new Int_node (5 )),

New Binary_node ("+", new Int_node (3), new Int_node (4 )));

Cout <* t;

// The node is not deleted.

}

 

Three types of definition Defects

The above solution has basically solved the problem, but the method for creating the expression tree still needs to be improved. According to the above scheme, we can only define the expression as follows:

Binary_node * t = new Binary_node ("*",

New Unary_node ("-", new Int_node (5 )),

New Binary_node ("+", new Int_node (3), new Int_node (4 )));

Cout <* t <endl;

There is still a gap between this improvement and our ideal expression, and we no longer have pointers to new objects in the inner layer. Therefore, the above Code may cause memory leakage, if we solve this problem by defining the destructor, the object may be deleted multiple times, because in ideal cases, multiple expr_nodes may point to the same lower-level expression object, in this way, all memory management tasks are handed over to the user.

 

4. Handle class improvement.

Since you only care about the tree, rather than a single node in the tree, you can use Expr to hide the inheritance hierarchy of Expr_node. Here we go back to the content of the handle class discussed above. Only Expr is visible to users, and the memory management is completely controlled by Expr! The improved code is as follows:

 

# Include <iostream>

# Include <string>

Using namespace std;

 

Class Expr_node

{

Friend class Expr; // The friend Meta class can be inherited.

Int use; // reference count

Public:

Virtual void print (ostream &) const = 0;

Public:

Expr_node (): use (1 ){}

Virtual ~ Expr_node (){}

};

 

Class Expr // handle class

{

Friend ostream & operator <(ostream & o, const Expr & e );

Private:

Expr_node * p; // pointer to the base class

Public:

Expr (int n );

Expr (const string & op, Expr t );

Expr (const string & op, Expr left, Expr right );

Expr (const Expr & t );

Expr & operator = (const Expr &);

~ Expr ()

{

If (-- p-> use = 0)

Delete p;

}

};

 

Class Int_node: public Expr_node

{

Private:

Int n;

Public:

Int_node (int k): n (k ){}

Void print (ostream & o) const

{

O <n;

}

};

 

 

Class Unary_node: public Expr_node

{

Private:

// Friend class Expr;

String op;

Expr opnd;

Public:

Unary_node (const string & a, Expr B): op (a), opnd (B ){}

Void print (ostream & o) const

{

O <"(" <op <opnd <")";

}

};

 

Class Binary_node: public Expr_node

{

Private:

// Friend class Expr;

String op;

Expr left;

Expr right;

Public:

Binary_node (const string & a, Expr B, Expr c): op (a), left (B), right (c ){}

Void print (ostream & o) const

{

O <"(" <left <op <right <")";

}

};

 

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: Expr (const Expr & t) {p = t. p; ++ p-> use ;}

 

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 & e)

{

E. p-> print (o );

Return o;

}

 

Void main ()

{

Expr t = Expr ("*",

Expr ("-", Expr (5 )),

Expr ("+", Expr (3), Expr (4 )));

Cout <t <endl;

 

}

 

Summary

This example shows the three elements of object-oriented. The classes designed in this example have good scalability. For example, to add nodes with multiple subnodes, you only need to add new classes, then add a new constructor to Expr. You do not have to know how the underlying code is implemented. This idea should be well used in the future when faced with problems!

 

Author: yucan1001

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.