头文件的布局
#ifndef __COMPLEX__
#define __COMPLEX__
class complex
{
...
};
#endif
一定要采用防卫式声明,保证头文件不被重复包含。
类的两种形式
类的定义分为有指针成员的类和无指针成员的类。
下面介绍complex类实现无指针成员类的定义,string类实现有指针成员类的定义。
complex类的定义
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&);
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
内联函数
有些函数直接定义在class body中定义,有些则在class body外定义。若函数定义在calss body中,则函数自动成为inline候选函数。定义在class body外的函数需要使用inline关键字建议编译器将其内联:
inline double
imag(const complex& x)
{
return x.imag ();
}
访问级别
访问级别共三种:public,private,protected。
数据通常要设置成private属性,从而达到对外隐蔽的作用,函数一般设置public属性,从而可以作为接口被外部调用。假设我们定义了上面的复数类。
采用下面的方式获取复数的实部和虚部是正确的:
cout << c1.real();
cout << c1.imag();
但是采用下面的方式获取复数的实部和虚部时编译器将会报inaccessible的错误:
cout << c1.re;
cout << c1.im;
构造函数
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
构造函数没有返回值也不需要返回值。
构造函数可以采用initialization list ,也是唯一可以采用这种初始化方式的函数,采用initialization list是一种高效的方式,推荐使用,而不要采用在构造函数内部再去初始化,这种方式效率低。
构造函数可以重载,但是下面这种重载的方式,编译器将会报has more than one default constructor的错误:
complex(double r = 0, double i = 0) : re(r), im(i) { } complex() :re(0), im(0){}
//调用时
complex c;
这是因为上面既定义了一个提供了默认参数的初始化,又提供了无参数的构造函数,实例化对象时将会产生歧义。
如果被构造函数放在private区:
private:
complex(double r = 0, double i = 0)
: re(r), im(i)
{ }
此时如果要实例化对象:
complex c1;
complex c2(2,1)
编译器会报:cannot access private member declared in class 'complex',说明将构造函数放在private区后将不能用来实例化对象。但是C++中有一种名为Singleton 的设计模式,就是将构造函数放在private区:
class A {
public:
static A& getInstance();
setup() { ... }
private:
A();
A(const A& rhs);
...
};
A& A::getInstance()
{
static A a;
return a;
}
常量成员函数
double real () const { return re; }
double imag () const { return im; }
常量成员函数说明,在这个函数中不会修改变量的值,一般建议通常只要函数不会修改变量的值都定义成常量成员函数,这样也可以避免下面的错误:
//定义函数时
double real() { return re; }
double imag() { return im; }
//调用函数时
const complex c(2, 1);
cout << c.real() << endl;
cout << c.imag() << endl;
这种情况就是使用意图和定义的意图不一致。
参数传递
参数传递的方式有:传值、传引用。
推荐使用传引用的方式,因为这种传递方式本质长传递的是指针(4byte),传递效率高,但是如果参数占用1byte、2byte则也可以采用传值得方式,效率更高一些。
通过引用传参,变量的值在函数中被修改将导致连锁反应,即参数值也会随着改变,如果我们不想参数值在函数中改变,可以传const 引用。
complex& operator += (const complex&);
返回值传递
返回值传递的方式:传值、传引用。
一般推荐传引用,这种方式效率更高,但并不是绝对的,有些情况下不得不传递值。
//返回值传引用
complex& operator += (const complex&);
//返回值传值
double real () const { return re; }
double imag () const { return im; }
当然在返回引用时也可以添加const。
友元函数
//class body中声明
friend complex& __doapl (complex*, const complex&);
//class body 外定义
inline complex&
__doapl (complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
友元函数能够自由的获取private成员。
友元函数破坏了类的封装性,但是相比于使用函数返回private成员的方式效率高。
相同class的object之间互为友元:
增加一个函数:
int func(const complex& param)
{ return param.re + param.im; }
调用时:
complex c1(2, 1);
complex c2;
cout<
操作符重载
在C++中操作符可以看成是一个函数,操作符重载可以分为两种形式,在calss body内的作为成员函数的重载,以及class body外作为全局函数的重载。
重载+=:
inline complex&
__doapl(complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex&
complex::operator += (const complex& r)
{
return __doapl (this, r);
}
//调用时
{
complex c1(2,1);
complex c2(5);
c2 += c1;
}
类的每个成员函数都包含一个this指针,参数传递时不需要显示指出来,这个this指针指向当前调用类成员函数的对象。
注意上面的两个函数的返回值之所以采用引用的形式,是因为可能在使用时出现下面的调用形式:
c3 +=c2 += c1
重载+操作符:
inline complex
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y),
imag (x) + imag (y));
}
inline complex
operator + (const complex& x, double y)
{
return complex (real (x) + y, imag (x));
}
inline complex
operator + (double x, const complex& y)
{
return complex (x + real (y), imag (y));
}
//调用时
complex c2;
c2 = c1 + c2;
c2 = c1 + 5;
c2 = 7 + c1;
这里需要注意的是,上面的三个函数,决不能返回引用的形式,这是因为typename()将创建临时变量,不能用引用来接收函数中返回的局部变量。
typename()将在函数内部申请临时的存储空间,一旦程序运行到typename()所在行结束,这个临时的空间就不存在了。
定义取正取负重载操作符
inline complex
operator + (const complex& x)
{
return x;
}
inline complex
operator - (const complex& x)
{
return complex (-real (x), -imag (x));
}
//调用时
complex c1(2,1);
complex c2;
cout << -c1;
cout << +c1;
这里不是加减号,是因为参与运算参数的个数只有1个,在这里是单目的取正和取负操作符。
注意取负函数不能返回引用形式的参数,这是因为它的返回值是局部变量。
非成员函数的操作符重载
inline bool
operator != (const complex& x,
const complex& y)
{
return real (x) != real (y)
|| imag (x) != imag (y);
}
inline bool
operator != (const complex& x, double y)
{
return real (x) != y || imag (x) != 0;
}
inline bool
operator != (double x, const complex& y)
{
return x != real (y) || imag (y) != 0;
}
//调用时
complex c1(2,1);
complex c2;
cout << (c1 != c2);
cout << (c1 != 2);
cout << (0 != c2);
重载<<:
#include
ostream&
operator << (ostream& os, const complex& x)
{
return os << '(' << real (x) << ','
<< imag (x) << ')';
}
<<是一个ostream对象,这里返回引用的形式是防止如下调用:
cout << c1 << conj(c1);
calss body外的各种定义
求共轭复数:
inline complex
conj (const complex& x)
{
return complex (real (x), -imag (x));
}
取复数的实部和虚部:
inline double
imag(const complex& x)
{
return x.imag ();
}
inline double
real(const complex& x)
{
return x.real ();
}
//调用时
complex c1(2,1);
cout << imag(c1);
cout << real(c1);
定义在class body外即为全局作用域函数。
完整的复数类定义
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__
class complex;
complex&
__doapl (complex* ths, const complex& r);
complex&
__doami (complex* ths, const complex& r);
complex&
__doaml (complex* ths, const complex& r);
class complex
{
public:
complex (double r = 0, double i = 0): re (r), im (i) { }
complex& operator += (const complex&);
complex& operator -= (const complex&);
complex& operator *= (const complex&);
complex& operator /= (const complex&);
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex *, const complex&);
friend complex& __doami (complex *, const complex&);
friend complex& __doaml (complex *, const complex&);
};
inline complex&
__doapl (complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex&
complex::operator += (const complex& r)
{
return __doapl (this, r);
}
inline complex&
__doami (complex* ths, const complex& r)
{
ths->re -= r.re;
ths->im -= r.im;
return *ths;
}
inline complex&
complex::operator -= (const complex& r)
{
return __doami (this, r);
}
inline complex&
__doaml (complex* ths, const complex& r)
{
double f = ths->re * r.re - ths->im * r.im;
ths->im = ths->re * r.im + ths->im * r.re;
ths->re = f;
return *ths;
}
inline complex&
complex::operator *= (const complex& r)
{
return __doaml (this, r);
}
inline double
imag (const complex& x)
{
return x.imag ();
}
inline double
real (const complex& x)
{
return x.real ();
}
inline complex
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y), imag (x) + imag (y));
}
inline complex
operator + (const complex& x, double y)
{
return complex (real (x) + y, imag (x));
}
inline complex
operator + (double x, const complex& y)
{
return complex (x + real (y), imag (y));
}
inline complex
operator - (const complex& x, const complex& y)
{
return complex (real (x) - real (y), imag (x) - imag (y));
}
inline complex
operator - (const complex& x, double y)
{
return complex (real (x) - y, imag (x));
}
inline complex
operator - (double x, const complex& y)
{
return complex (x - real (y), - imag (y));
}
inline complex
operator * (const complex& x, const complex& y)
{
return complex (real (x) * real (y) - imag (x) * imag (y),
real (x) * imag (y) + imag (x) * real (y));
}
inline complex
operator * (const complex& x, double y)
{
return complex (real (x) * y, imag (x) * y);
}
inline complex
operator * (double x, const complex& y)
{
return complex (x * real (y), x * imag (y));
}
complex
operator / (const complex& x, double y)
{
return complex (real (x) / y, imag (x) / y);
}
inline complex
operator + (const complex& x)
{
return x;
}
inline complex
operator - (const complex& x)
{
return complex (-real (x), -imag (x));
}
inline bool
operator == (const complex& x, const complex& y)
{
return real (x) == real (y) && imag (x) == imag (y);
}
inline bool
operator == (const complex& x, double y)
{
return real (x) == y && imag (x) == 0;
}
inline bool
operator == (double x, const complex& y)
{
return x == real (y) && imag (y) == 0;
}
inline bool
operator != (const complex& x, const complex& y)
{
return real (x) != real (y) || imag (x) != imag (y);
}
inline bool
operator != (const complex& x, double y)
{
return real (x) != y || imag (x) != 0;
}
inline bool
operator != (double x, const complex& y)
{
return x != real (y) || imag (y) != 0;
}
#include
inline complex
polar (double r, double t)
{
return complex (r * cos (t), r * sin (t));
}
inline complex
conj (const complex& x)
{
return complex (real (x), -imag (x));
}
inline double
norm (const complex& x)
{
return real (x) * real (x) + imag (x) * imag (x);
}
#endif //__MYCOMPLEX__