开发手记

学了一年半的c++,整天吼着说什么这不是面向对象,那不符合c++ standard,真正准备写一个类出来的时候,才明白自己是多么的无知。
我早就准备尝试做做符号运算,苦于不知道用来存储表达式的树该怎么来写,一直搁浅。
从小事做起,我想先从多项式做起,这个很简单。
于是,今晚开始动工!
嗯,先设计第一个类,MonomialExpr,单项式表达式。
首先,最核心的两个数据成员,次数和幂数,想了想,还是设为了private.但愿我有能力把它完全封装在内。
什么类型呢?double?为了以后扩展需要,还是不要声明类型了。我想等我再学会点Blitz++后试试那些huge 的浮点型。我可不想告诉我的用户:“对不起,c++的内置数据类型最大只为double,所以此程序在您的32位x86 PC机上最大只能处理64位的数据!”
private:
COEFFICIENT_TYPE coefficient_;
EXPONENT_TYPE exponent_;
Ok,下来怎么处理这两个类型?按我以前的方式,都是放在一个config.h中,用宏来定义
#define COEFFICIENT_TYPE double
呵呵,不是说c++中的模板除了有点像类外就是宏吗?好吧!试试,复杂的类型我不会写type_traits,这些简单的double,float我还是会处理的。
好,加上默认构造函数,拷贝构造函数,析构函数,它大致就是这样了

/*! \class MonomialExpr 
* 
*/ 
template 
class MonomialExpr{ 
public: 
/// Default constructor; 
MonomialExpr(){ 
}; 

/// construct by a given coefficient and exponent; 
MonomialExpr(COEFFICIENT_TYPE coefficient,EXPONENT_TYPE exponent): 
coefficient_(coefficient), 
exponent_(exponent) 
{ 

}; 

/// Default destructor; 
~MonomialExpr(){ 
}; 

/// Copy constructor 
MonomialExpr(const MonomialExpr&){ 
}; 

private: 
COEFFICIENT_TYPE coefficient_; 
EXPONENT_TYPE exponent_; 
}; 

typedef MonomialExpr MNExpr;

我想,如果我下次需要更改系数和幂数的类型,改下最后一句定义就行了。而MonomialExpr是不允许用户直接访问的。
哎,早知道,就把MonomialExpr定义为MonomialExprImpl,最后留给用户的是一个可读名称,那该多好。
懒得改,算了!
好吧,下面,难点来了,如何重载<我需要用户这样来使用它
MNExpr expr(2.5,25.6);
std::cout<嗯,operator << 的原形是什么?
能回答出这个问题的估计都是c++高手了

basic_ostream& operator<<( 
basic_ostream& (*_Pfn)(basic_ostream&) 
); 
basic_ostream& operator<<( 
ios_base& (*_Pfn)(ios_base&) 
);

这是在basic_ostream中operator << 的原形定义。
我们应该如何去重载呢?
唉!
首先,上面看见的,应该是全局的定义。如果我把其作为一个全局函数来重载,那么我typedef MonomialExpr MNExpr;的strick就玩完了!
崩溃了!怎么办?
再查!
事实上,如果要对Class X重载operator<<,应该这样定义
std::ostream &operator<<(std::ostream &, X const &);
看起来是不是很奇怪?郁闷!
想起一个我从来没有用过的关键字: Friend!
我讨厌它!不错,我就是一个独行客,我就是不希望有任何Friend。能独自完成的就独自完成,不能的就找别人帮忙。要么继承自它,要么拥有它,要么使用它。干嘛弄个不明不白的Friend关系呢?但是,今天,
5555555555555555555555555
行,我认输。我终于明白,原来Friend也有Friend的好。
friend std::ostream &operator<<(std::ostream& os, const MonomialExpr & expr){
os << expr.coefficient_ << "x^" << expr.exponent_;
return os;
};
Ok,测试,成功!
唉!如果你以为重载 operator+就是这么简单,那也不用听我在此浪费口水了。

/// unformated output 
friend std::ostream &operator<<(std::ostream& os, const MonomialExpr & expr){ 
std::ios_base::iostate state = 
std::ios_base::goodbit; 
const std::ostream::sentry ok( os ); 
if (!ok) 
state |= std::ios_base::badbit; 
else 
{ 
try{ 
os << expr.coefficient_ << "x^" << expr.exponent_; 
} 
catch ( ... ){ 
try 
{os.setstate( std::ios_base::badbit ); } 
catch ( ... ) 
{} 
if ( ( os.exceptions( ) & std::ios_base::badbit ) != 0 ) 
throw; 
} 
} 
os.setstate( state ); 
return os; 
};

这是改过的。
我已经累得没有力量去解释为什么要这么改了。
总之,重载时必须遵守原有的规范。如果对于iostream你已经使用熟练的话,你就会明白上面那些try…catch…throw是多么的必要。算了,不说了!
还是那句话,
如果你以为重载 operator+就是这么简单,那也不用听我在此浪费口水了!
namespace,对!看见吗?我一直不提倡使用using namespace std.
因为现在除了ACE,wxWidget这样的库考虑更广泛的可移植性,基本上是一个库一个namespace.想想,如果我把上面的类放在一个namespace NM中。。。
My God!
理论上来讲,你必须这样来引用它: NM::operator<<(std::cout, x);
哈哈,所有人都会崩溃!
于是,我们需要修改编译器来为我们写的代码提供些便利!^_^
所以,你注意到没?
basic_ostream的operator<std::operator<<(std::cout, x);
!!!
对,函数的参数类型,能决定函数的作用域!
如此破天荒的规定,估计也 只有CPPer才能想的出来!
更好的解决方案,我还没想出来,如果你能找到一个完美的解决方案,哪怕需要把我现有的代码增长一倍,我也感谢你 !
好吧,让cout的问题就此中止。
理论上来讲,我还需要一个operator>>使得我能利用它直接把从键盘输入的字符串parse成该类的一个实例,算了,不管了。而且,这样的东西应该写在构造函数中才对。
好吧,下一个。
operator +
成员?全局?
无休止的争论。反正总之,在此我更倾向于把一些都写在那个该死的类体中,不然,我自己也会崩溃的。
或者,如果真的必要,我得把最后那句typedef改改。
OK

/// operator + 
/// note:their exponent must be equal; 
MonomialExpr operator+(const MonomialExpr& expr) const{ 
if(this->exponent_ == expr.exponent_){ 
return MonomialExpr(expr.coefficient_ + this->coefficient_,this->exponent_); 

} 
else{ 
/// throw an exception. 
; 
} 

}

来看看吧,这个异常怎么抛出。
我以前的处理方式,delete this,然后让用户去检测返回值。嗯。。。可是,我能返回一个空引用吗?不可能。那么,用户就不能通过检测返回值来得知该操作是否成功。那么,像iostream一样设置一个公有成员变量标志位来标示是否成功?还是添一个接口successed()?
算了,throw 还是最佳的选择。
算了,还是 直接
throw "different exponent";
这样比较简单。
测试,出错,返回临时变量。
555555555555555555
明白!不能返回引用,不然就是先析构,后构造。应该按值返回。就是先构造,后析构。
继续,operator +=

/// operator += 
/// note:their exponent must be equal; 
MonomialExpr& operator+=(const MonomialExpr& expr){ 
    if(this->exponent_ == expr.exponent_){ 
        this->coefficient_ += expr.coefficient_; 
        return *this; 
    } 
    else{ 
        /// throw an exception. 
        throw "different exponent"; 
    } 
}

类似的,继续实现-,-=,*,*=就行了
单项式有了,那么就该多项式了。所谓的多项式,就是很多个单项式的和嘛!
嗯,考虑到化简合并经常需要插入删除项,还是用链表吧!

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥