在编写类的时候我们要很好的把握细节问题,不仅仅要去避免一些明显的错误,更多的是如何形成良好的编程风格。下面我们将从下面的一个例子分析类的编写技巧:
class Complex
{
public:
Complex(double real,double imaginary = 0):_real(real),_imaginary(imaginary){ }
void operator+(Complex other)
{
_real =_real +other._real;
_imaginary = _imaginary + other._imaginary;
}
void operator<<(ostream os)
{
os << "(" << _real << "," << _imaginary << ")";
}
Complex operator++()
{
++_real;
return *this;
}
Complex operator++(int)
{
Complex temp = *this;
++_real;
return temp;
}
private:
double _real,_imaginary;
}
1、我们要当心在隐式类型转换中产生的隐含临时对象。避免这个问题的一个好方法就是,尽可能的在构造函数中进行显示的类型转换,并要避免编写类型转换运算符。
由于上述类中的构造函数的第二个参数有默认值,因此这个函数也可当成是单参数的构造函数来使用,这也意味着可以进行从double类型到Complex类型的隐式转换。要注意这种类型转换可能并不总是我们所希望的。
2、在传递参数对象时,我们应该优先选择const&的方式,而不是传值方式。例如:
void operator+(Complex other)在传递参数时将会有临时对象的产生,执行效率会降低
3、我们应该优先选择“a op=b”这种写法,而不是“a = a op b”(这里的op表示的是某个运算符)。这种写法更为清晰,而且效率也更高。
为什么operator+=的效率会更高?原因就在于这个运算符是直接对其左边的对象进行运算,并且返回的是一个引用而不是临时对象。而operator+则必须返回一个临时对象。例如:
T& T::operator+=(const T& other)
{
//...
return *this;
}
const T operator+(const T& a,const T& b)
{
T temp(a);
temp+=b;
return temp;
}
我们要注意的是运算符+和+=之间的关系。前者是通过后者来实现的,这不仅是为了实现代码的简洁性,也是为了保证代码的一致性。注意,为了防止程序员编写出像“a+b = c”这样的表达式,所返回的临时对象的类型应该是“const Complex”(而不是Complex),这就是为什么上面的operator+中返回的是const T的原因。
4、在C++标准中规定:必须将运算符=、( )、[ ]和->定义为成员函数,而在类中定义的new、new[]、delete和delete[]等运算符函数必须是静态成员函数。对于其他的运算符函数:如果运算符函数时用于对流进行I/O的operator>>或者operator<<;或者如果需要对其左参数进行类型转换;或者如果可以通过类的公有接口来实现,那么将这个函数定义为非成员函数(在前两种情况中,如果需要的话也可以被定义为友元函数)。如果运算符函数需要实现虚函数的行为,那么请增加一个虚成员函数来提供虚函数的行为,那么请增加一个虚成员函数来提供虚函数的行为,并用这个虚成员函数来实现运算符函数。否则将运算符函数定义为成员函数。
5、operator<< 不应该被定义为成员函数。并且,非成员函数operator<<应该使用成员函数(通常是虚函数)来实现,并且这个成员函数的功能就是进行流输出。更进一步,operator<<应该返回一个“ostream&”类型的应用,并且所应用的就是这个流对象,这是为了实现链式操作。
6、前置递增运算符应该返回一个非常量的引用,这不仅使客户代码能够以更直观的方式来编写,而且还避免了不必要的低效率。在后置递增运算符函数中应该返回一个常量值。这种做法可以防止对返回的对象进行修改,从而避免了想“a++++”这样的问题代码。并且为了保持代码的一致性,我们应该使用前置递增来实现后置递增。
7、要避免使用保留名字。在C++标准库的实现中保留了一些带有前导下划线的标识符,这些保留的标识符是很难记住的,因此,最好是根本不要使用前导下划线。
经过以上的分析,我们修改后的类为:
class Complex
{
public:
explicit Complex(double real,double imaginary = 0):_real(real),_imaginary(imaginary){ }
Complex& operator+=(const Complex& other)
{
_real += other._real;
_imaginary += other._imaginary;
return *this;
}
Complex& operator++()
{
++_real;
return *this;
}
const Complex operator++(int)
{
Complex temp (*this);
++_real;
return temp;
}
ostream& Print(ostream& os) const
{
return os << "(" << real_ <<","<< imaginary_ << ")";
}
private:
double real_,imaginary_;
}
const Complex operator+(const Complex& lhs,const Complex& rhs)
{
Complex ret(lhs);
ret+=rhs;
return ret;
}
ostream& operator<<(ostream& os,const Complex& c)
{
return c.Print(os);
}