首页 > 代码库 > 类在编写过程中的一些注意事项

类在编写过程中的一些注意事项

在编写类的时候我们要很好的把握细节问题,不仅仅要去避免一些明显的错误,更多的是如何形成良好的编程风格。下面我们将从下面的一个例子分析类的编写技巧:

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

}



类在编写过程中的一些注意事项