PoEdu - C++项目班 【Po学校】-Lesson02 -面向对象&多态&递归下降- 课堂笔记
对象语义实现的两种方式
1--> Node 系列类实现细节
1.0.0 为什么要把Node类的析构写成纯虚函数?又为什么把Node构造函数加权限隐藏呢?因为我们要设计成:只能Node的子类来进行new出来,并且不让它的对象拷贝构造,拷贝赋值。如此Node类下面的派生类,只能new出新对象,些对象具有唯一性,不可赋值,不能拷贝。如此这般特性:1,不能构造的只能new出对象设计;2,不可拷贝构造 ;3,不可拷贝赋值;4,可以被父类指针指向。具有这4个特点的类设计,就是“面向对象”的语义。
1.0.1 在Node类里面设计一个Calc()抛出deboule数值,它被设计成纯虚函数;在Node(节点)类中不需要实现它,我们可以把它看成一个路由功能(父类指针)的函数;当多态得以实现,每一个子类都有这个抛出数值的,带有路由功能的函数。
1.0.2 设计一个Node的派生类NumberNode,记得一定要实现calc(),否则些类将不可用。
1.0.3 设计一个二元类派生类BinaryNode,用此节点来存储两个元素的表达式,这个节点只有两个Node*类型的成员元素;表达式最终的计算是两个元素之间的运算,抽象出这个二元类,也是针对常量表达式计算规律的本质;为什么2个元素成员都是Node*类呢?因为Node类是系列子类的父类,可以用父类指针指向系列子类,多态特性如此。BinaryNode也继承了Node节点,也是可以利用多态的特性,以Calc()函数路由指向。这个类的精华点不仅仅如此,还有其2个成员,设计成父类Node类型的指针,也是此节点设计的一个精华所在:父类指针在父类中,代表了自己,它是一,从虚无中生出:类是虚的,new出对象来才是实体;一分二:两个可以指向父类所有派系的指针;两个指针可以是:一个元素,和一个表达式;所有常量表达式都可以解析了。
1.0.4 那么,到此时AddNode节点,应该继承哪个类?Node?还是二元类BinaryNode?看起来Node也可以选择,但是想一想:继承二元类BinaryNode是更合适的选择。因为继承了二元BinaryNode类,那么AddNode里面也就包含了父类BinaryNode的2个成员,也就包含了“三生万物”的特性。“三千大道,我只取一瓢而饮。”分析两边元素,最后返回相加的和。
1.0.5 作业:1 完善减MinusNode、乘MultipliNode、除DivideNode子类设计,思考一下:在哪里new,在哪里释放?
1.0.6 2 除法应该注意的是除数不能为0,那么怎么判断浮点数是否为0?
1.0.7 3 一元节点(负)MinusNode要怎么设计?如何返回?
1.1.0 具体代码实现Node系列子类(略)
1.2.0 如此设计一个类,它有什么作用?
class NonCopyable { protected: NonCopyable(){} private: NonCopyable(const NonCopyable&); const NonCopyable& operator=(const NonCopyable&) = delete; }
1.2.1 1 上面类权限上有特点:拷贝构造与拷贝赋值都是pvivate,是子类不可见的。2 构造函数权限是protected,也就是说类外部是不可以构造对象的,那么就只能new出对象了。这正是面向对象的语义,如果一个类想达到面向对象的语义,可以继承这个类。
1.2.2 写代码注意细节 1 命名空间 2 尽量的多用const 3 注意基类的虚函数设计:要在子类实现的函数,一般多有纯虚的析构函数等。4 注意添加 override关键字。
1.2.3 分析:在哪里new出,表达式解析时new出;在哪里释放?在二元节点BinaryNode处释放,只有二元节点才知道什么时候释放。
1.2.4 对接口进行继承用private继承;
1.2.5 在子类中直接调用父类的构造方法如:
//如下面代码中,AddNode的构造函数:AddNode(Node *left, Node *right) :BinaryNode(left, right){}
class BinaryNode:public Node { public: BinaryNode(Node* left, Node* right):left_(left),right_(right){} ~BinaryNode(); protected: Node *left_; Node *right_; }; class AddNode:public BinaryNode { public: AddNode(Node *left, Node *right) :BinaryNode(left, right){} //直接调用父类的构造方法赋值 };
1.2.6 注意浮点数判断除数是否为0的写法:判断一个区间。记住一个头文件<cfloat>,DBL_EPSILON这个是常用的最小值精度。
#include <cfoat>
double DiviNode::Calc() const { double divisor = right_->Calc(); if (divisor < -DBL_EPSILON || divisor > DBL_EPSILON) { divisor = left_->Calc() / right_->Calc(); } return divisor; }
1.3.0 一元因子UnitaryNode类的实现
1.3.1 和二元类BinaryNode类似,一元因子也要承担起释放的任务。
1.3.2 实现一元UnitaryNode节点的子类