C++设计一个不含指针的类相对设计一个含指针的类来说简单一些,因为它不需要考虑一下三大因素(也叫Big Three): 析构函数(destructor) 拷贝构造函数(copy constructor) 拷贝赋值函数(copy assignment operator) 如果我们定义一个类的时候没有给出这3个函数,那么C++的编译器会自动生成四个缺省函数如下:
A(void); // 缺省构造函数 A(const A &a); // 缺省拷贝构造函数 ~A(void); // 缺省析构函数 A & operate =(const A &a); // 缺省拷贝赋值函数这些函数都是基于memberwise copy - 因为没有指针,所以不会涉及到同一块内存被多个对象指向的问题。析构的时候也不需要考虑内存的操作。
举例complex类如下:
class complex //class head { //class body 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&); };这里有几个需要注意的地方:
real()和imag()在类的体内定义,则自动成为inline函数。如果在体外定义,则需要显式声明inline才会成为inline函数。
friend函数本身不是类的一部分,friend是指此函数可以自由访问该类的private成员。friend也可以用在类上面,一个类的friend类的object可以访问该类的private成员。注意: 同一个类的各个object互为friend,这也是为什么拷贝构造函数和赋值构造函数可以实现的基础。
friend函数有什么好处呢?因为friend函数不需要通过class来访问,比用class函数来做效率更高。
complex定义了自己的缺省构造函数。 complex (double r = 0, double i = 0): re (r), im (i) { }所谓缺省构造函数是指对象的创建不需要参数就可调用的构造函数。C++标准规定,如果构造函数没有参数,或者构造函数的所有参数都有缺省值,则算作缺省构造函数。
如果complex没有定义自己的缺省构造函数,C++编译器会自动给它生成一个缺省构造函数类似于 complex() {} 注意该函数什么也不做。
如果complex的缺省构造函数为 complex(double r=0, double i=0): re®, im(i) {}, 并且没有其他缺省构造函数,代码 complex c1; 会调用该构造函数,并用re和im的缺省参数。
如果complex的构造函数为 complex(double r, double i): re( r ), im( i ) {}, 并且没有其他缺省构造函数,代码 complex c1; 会编译出错,因为编译器不知道把什么值赋给re和im。
如果complex的缺省构造函数为 complex() {}, 并且没有其他缺省构造函数,代码 complex c1; 会调用该缺省构造函数。
如果complex 有两个缺省构造函数, complex() {} complex(double r=0, double i=0): re( r ), im( i ) {} 代码 complex c1; 编译会出错,因为编译器不知道调用那个缺省构造函数。
如果complex有以下两个构造函数,注意第一个为缺省构造函数,第二个不是。它们可以共存。 complex() {} complex(double r, double i): re( r ), im( i ) {} 代码 complex c1; 编译会通过,编译器会采用complex() {}作为缺省构造函数。
如果complex没有定义拷贝构造函数,那么 complex c1=complex(); 和 complex c1 = c2; 都会调用complex的缺省拷贝构造函数(也就是memberwise copy)。注意第一个例子里面的complex()会导致调用缺省构造函数生成一个complex的临时对象,然后代码再调用comlex的缺省拷贝构造函数进行memberwise copy。第二个例子则只需调用缺省拷贝构造函数一次。
如果complex没有定义拷贝赋值函数,那么 c1 = c2; 会调用complex的缺省拷贝赋值函数。
下面几种写法要特别注意它们的区别: complex c1(); 声明一个名为c1的函数, 该函数返回complex类型。注意这里不是创建complex 类的对象! 切记!!! complex c1; //创建一个complex的一个名为c1的obj, 并且会调用complex 类中的缺省构造函数,其缺省参数为(0,0). complex(); //创建一个complex的临时对象,此处会调用complex 类中的缺省构造函数,同样,int()也是创建一个int的临时对象 complex c1=complex(); //创建一个complex的临时对象,并赋给c1,此处会调用缺省构造函数和缺省拷贝构造函数 complex c1=complex(3.0, 4.0); ** //创建一个complex的临时对象,并赋给c1,此处会调用构造函数和缺省拷贝构造函数** 注意这里也可以写成complex c1(3.0, 4.0), 会调用构造函数。 complex *p=new complex();//创建一个complex的obj,这里会调用complex类中的缺省构造函数,并用指针p指向它。注意,这两种写法都可以: complex c1 = complex(3.0, 4.0); //构造函数和缺省拷贝构造函数 complex c1(3.0, 4.0); //构造函数only ?
complex重载了+=操作符。操作符重载可以看成是一个特殊的函数,给C++编程带来很大便利。操作符重载可以是类成员函数,也可以是非类成员函数。如果是object自身与complex的其它object相加,可写成类成员函数,如上面的这种情况; 但如果不是object自身与complex其他object相加,则不能写成类成员函数。
如果是成员函数的话,默认第一个参数是this指针,通常不写。注意:操作符重载一定是作用在左边的操作符。
上面operator+重载函数参数采用传引用。 对于函数入口参数,尽量用传引用,因为传值的话,value会放进stack,应该尽量少用。 另外,对于函数返回值也是尽量返回reference,但如果函数返回的是在栈上分配的一个local变量或object,则不能返回reference,因为该local变量或obj已经被删除。
上面operator+的入口参数加了const。对于函数入口参数,如果函数不会改变这个参数,则该参数可以加const。 另外,如果某类函数不会改变函数的数据成员,则可以在函数名后加const,比如上面的real()和imag()函数。
C++ 的构造函数和析构函数都没有返回值!切记!
拷贝构造函数的参数一定要加&(即传引用),如果是传值,形参复制到实参的时候又会调用拷贝构造函数,这样就形成永无止境的递归调用而导致栈溢出。
