Complex类

C++类中最具有代表性的便是complex类和string类,complex类中的变量仅仅包含最基本的元素,基于简单的complex类实现介绍C++部分特性。

#ifndef __COMPLEX__

#define __COMPLEX__         //防御式头文件

class complex

{

    public:

       complex (double r = 0, double i = 0)

           :re(r), im(i)   //初值列表

       {}

       complex& operator += (const complex&);  //成员函数

       double real() const { return re; }      //const问题

       double imag() const { return im; }      //inline function

 

    private:

       double re, im;

       friend complex& __doapl(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)   //成员函数this隐藏参数

{

    return __doapl(this, r);         

    //local object用引用传递

    //运算符右侧变量不改变加const

}

inline complex

operator + (const complex& x, const complex& y)

{

    return complex( real(x) + real(y),

                   imag(x) + imag(y) );

    //结果是local object,只能用value传递

    //临时对象

}

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

}

#endif

首先通常我们将类定义在独立的头文件中,此处我们将complex类定义在complex.h头文件中,通过头文件#include “complex.h”方式插入到main.c文件中。在编译过程中,编译器将会自动将complex.h头文件中代码插入到main.c中,这里的插入过程是硬性插入,所以某个头文件被声明多次,则会出现重复定义的情况。为了防止此类情况发生,我们定义头文件时常采用防御式头文件定义方式。简而言之就是将头文件定义成如下形式:

#ifndef __COMPLEX__

#define __COMPLEX__

XXXXXXXXXXXXXX

#endif

若头文件第一次出现时,ifndef为真,执行define语句并一直运行到endif为止,当头文件第二次出现时,就会因为ifndef为假而终止头文件重复插入,避免头文件重复定义问题。

对于类的设计,为了封装性,通常会对类中元素的权限进行区分,分别是publicprivateprotect三种。complex类中只出现了publicprivate两种,通常情况下我们将类中的基本数据元素定义为private,防止类中数据被直接修改以保证安全性。对于类中数据操作的函数通常定义为public,此时类中被定义的函数(成员函数)可以被直接调用而不受限制。

类中public下第一个函数是complex类的构造函数,构造函数的作用是给类在定义时进行初始。构造函数相对于一般的函数有以下几点不同:

1.构造函数的函数没有返回值类型;

2.构造函数的函数名必须和类名相同;

3构造函数只在创建类时使用一次;

4.构造函数可以有初值列表用来完成初始化工作;

5.即使我们自己没定义,也会自动生成空参构造函数。

另外需要注意的是,C++中构造函数也支持重载,一个类可以有多个构造函数,通过参数的不同来区分具体用哪个构造函数来完成类的初始化。具体到本例中,构造函数complex在函数参数列表中使用了默认初值将ri两个变量设0,这样即使在创建类时没有输入任何参数也会将0作为默认值用于类中变量的初始化。在参数列表之外的部分是构造函数所特有的部分,称为初值列表(参数列表之后,函数体之前),其中re(r), im(i)语句相当于re = r; im = i。但是相比于将这两句定义在构造函数的函数体中,使用初值列表的区别在于:初值列表的赋值操作发生在函数体代码执行之前,所以像常量成员变量和引用类型成员变量必须使用这种方式完成赋初值工作。

public中第二函数完成了运算符重载的工作。类中只是进行了简单声明,其函数定义部分写在类外,不过写在类外的函数要通过complex::方式来加以说明,此处我们定义了C++中‘+=’运算符的重载操作。此操作完成将右侧的值与左侧相加并将结果写入左侧变量中,只不过之前都是对基本数据类型进行操作,此处是对复数进行的操作。这里有两点需要着重强调一下的就是:

1.+=’操作中右值不发生改变,所以传入的参数前最好加上const用来防止对数据的更改。

2.这个函数的参数列表中缺损一个complex类成员变量的引用,这是因为非静态成员函数访问非静态成员时,编译器会自动将对象本身地址作为参数传递给函数。就是说在使用右值时,会有隐含的this指针指向它,可以通过this指针完成对该变量的操作,并且可以将这里的this指针作为参数传递给调用函数,此例中就将this指针传递给了__doapl(this, r)函数。

在复数的诸多操作中,除了两个复数类型直接相加的情况,还有可能时复数的实部或虚部单独和某个数相加。所以又单独定义了多个操作符重载的情况,相比于前一种情况有两种明显不同。首先,除了上一个提到的运算符重载之外,剩下的重载函数都是通过全局函数来定义的,函数声明并未出现在类中。另外,它们最终的结果并不存储在参与运算的某个变量中,所以在进行操作时要在调用函数中先创建一个临时变量(在return语句中)用来完成计算工作,等完成计算之后再传递回来。因为在调用函数中创建的是临时变量,因为临时变量定义在栈上,当函数返回时会自动弹栈销毁临时变量,所以函数返回值不能够采用引用传递。

Advertisements