C++面向对象(二) Complex对象

  • 时间:
  • 浏览:
  • 来源:互联网

C++面向对象(二)Complex对象

    • 一、Header(头文件)防卫式声明
    • 二、Header(头文件)的布局
      • class declaration & definition
        • (1)内联函数——inline functions
        • (2)构造函数——constructor(ctor)
          • ① 初始化
          • ② 重载(overloading)
          • ③ 常量成员函数(==const== member functions)
          • ④ 参数传递(pass by ==value== or ==reference(to const)==)
          • ⑤ 返回值传递(return by ==value== or ==reference(to const)==)
          • ⑥ 友元(friend)

一、Header(头文件)防卫式声明

complex.h

 ———————————————————————————————————————————
|	#ifndef __COMPLEX__						|
|	#define __COMPLEX__						|
|	 ———————————————————					|
|	|		            |					|
|	|		            |					|
|	| ...               |					|
|	|		            |					|
|	|		            |					|
|	|		            |					|
|	 ———————————————————					|
|	#endif									|
 ———————————————————————————————————————————

上面头文件内部要写入的内容,便是用来处理下面主程序main()函数部分的。

complex-text.cpp
#include <iostream>
#include "complex.h"
using namespace std;

int main(){
	complex c1(2,1);
	complex c2;
	cout << c1 << endl;
	cout << c2 << endl;
	
	c2 = c1 + 5;
	c2 = 7 + C1;
	c2 = c1 + c2;
	c2 += c1;
	c2 += 3;
	c2 = -c1;
	
	cout << (c1 == c2) << endl;
	cout << (c1 != c2) << endl;
	cout << conj(c1) << endl;
	return 0;
}

二、Header(头文件)的布局

	complex.h
	———————————————————————————————————————————————————————————————————————————————
	|	#ifndef __COMPLEX__		        										  |
	|	#define __COMPLEX__		      											  |
	———————————————————————————————————————————————————————————————————————————————
0	|	#include <cmath>		          			--------------------------	  |
	|												|  forward declarations  |    |
	|	class ostream;             					|	   (前置声明 )	     |	  |
	|	class complex;		              		    --------------------------	  |
	|																			  |
	|   complex& __doapl (complex* ths, const complex& r); 		           		  |
	———————————————————————————————————————————————————————————————————————————————
1	|	class complex{	          					------------------------	  |
	|		...										|  class declarations  |      |
	|	};            								|	   (类-声明 )	   |      |
	|			              		   				------------------------	  |
	———————————————————————————————————————————————————————————————————————————————
2	|	complex::function ...	          		    ----------------------		  |
	|												|  class definition  |        |
	|	           									|	  (类-定义 )	 |    	  |
	|			              		   				----------------------		  |
	———————————————————————————————————————————————————————————————————————————————
	|	#endif	          					 									  |
	———————————————————————————————————————————————————————————————————————————————

主要部分是头文件的类声明类定义部分,而前置声明部分主要是为了给类声明类定义部分提供一些前置变量声明的准备。

class declaration & definition

class complex{														(class head)
———————————————————————————————————————————————————————————————————————————————
public:																(class body)
	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& __doaple(complex*, const complex&);
};
{
	complex c1(2, 1);
	complex c2;
}

由上面可知,
有些函数可在body里直接定义,如imag()real()
有些是先在body里声明,然后再在body外定义,如 ”+=“ 的重载、友元函数 __doaple()

一般来说,类对象的数据 建议 应该放在private内,防止被随意修改调用;而对于希望被外界使用的函数,则 建议 放在public内,方便外界调用类对象的函数。

同时,需注意,如果需设置另一个复数(complex)类型,其数据是由int型的re、im构成,如果按以上代码编写,则又需另外构造一个complex1类对象了(即将double → \rightarrow int);再换成数据由float型的re、im构成,则又需构造complex2…

所以引入 模板 template ,用一个类对象 complex 就可表示多种数据集成类型的对象。
---------------------------------------------------- ⇓ \Downarrow -----------------------------------------------------------

template <typename T>
class complex{														
public:																
	complex(T r = 0, T i = 0): re(r), im(i) {}
	complex& operator += (const complex&);
	T real() const{
		return re;
	}
	T imag() const{
		return im;
	}
private:
	T re, im;
	
	friend complex& __doaple(complex*, const complex&);
};
{
	complex<double> c1(2.5, 1.5);
	complex<int> c2(2, 6);
}

在类对象前面先声明 template< typename T>,说明T是个type,是 未定的 (也可以换成其他的字母P、X等),等使用者complex c1(2.5, 1.5) 需要使用 double类型 就将 T 绑定为对应的 double 类型。

(1)内联函数——inline functions

内联函数的优点是执行快 。而函数若在class body内定义完成,便自动成为inline的候选人,但不是一定的 ,是否可以成为inline函数仍需要编译器自己去选择判断

分界点在:看函数是否复杂,不复杂则可转化为inlie函数。

在上面的例子中,因为real()、imag()函数是在class body里面定义的,且内容简单不复杂,所以其为inline函数。我们也可以选择不在class body内定义,而在body外定义real()、imag()为inline函数,只需在前面添加关键字 inline即可。但我们需要注意,我们添加inline关键字在函数前面,只是建议编译器尽量将其转为内联函数而已,但实际上是不是转为inline函数,是编译器自己去判断的

inline double imag(const complex& x){
	return x.imag();
}

(2)构造函数——constructor(ctor)

构造函数也是可以放在private里面的

① 初始化

表达一(构造函数独有的表达形式):

complex(double r = 0, double i = 0) : re(r), im(i) {} 

表达二:

complex(double r = 0, double i = 0) {
	 re = r;
	 im = i; 
}

两种表达方式都可以达到完成赋值的效果,但表达方式一效率更高,简单一点分析的话,就是因为一个变量或对象其数值的设定有两个阶段一是初始化阶段,二是赋值阶段 ,表达二中放弃了赋初值(初始化)的阶段,而选择在{}里再赋值,时间上晚了些,代码效率便也差了些。

② 重载(overloading)

C中是不允许相同函数名称却有多个的,而C++中是允许存在一个以上的相同函数名称的函数的(函数的重载)。

如 上面代码中,可同时存在同名函数real()

double real() const {
	return re;
}
void real(double r){
	re = r;
}

两者的实现功能不一样,前者是取出实部,后者是设定实部,且两者编译后的实际名称是不一样的:

?real@Complex@@QBENXZ
?real@Complex@@QAENABN@Z

但下面情况下,构造函数的同名函数重载是不能同时存在的,因为两者作用一样,编译器会无法判断到底该去调用哪一个函数:

complex(double r = 0, double i = 0) {
	 re = r;
	 im = i; 
}
complex(double r = 0, double i = 0) : re(r), im(i) {} 
  • 操作符重载(成员函数,有this指针
    每个成员函数都有个隐藏的this pointer(指针),其指向调用者,谁调用这个函数就指针就指向谁
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);
}

int main(){
	complex c1(2, 1);
	complex c2(5);
	c2 += c1;
}

这里实际上在重载操作符 inline complex& complex::operator += (const complex& r) 时,括号里面的参数隐藏了个this指针的,其将c2的地址赋给this指针,将c1赋给r

inline complex& complex::operator += ("this", const complex& r){
	return __doapl(this, r);
}
  • 操作符重载(非成员函数,无this指针
{
	complex c1(2, 1);
	complex c2;
}
c2 = c1 + c2;
inline complex operator + (const complex& x, const complex& y){
	return complex(real(x) + real(y), imag(x) + imag(y));
}
c2 = c1 + 5;
inline complex operator + (const complex& x, double y){
	return complex(real(x) + y, imag(x));
}
c2 = 7 + c1;
inline complex operator + (double x, const complex& y){
	return complex(x + real(y), imag(y));
}

注意:上面这些重载函数是不可return by reference,因为他们返回值是个locall object,代码语句执行到下一行就消灭掉了,所以返回值引用传递的话会出错。其操作将两个objects相加起来,然后在函数体内创建出新的原本不存在的空间去存放相加的结果,在函数里才创建的新东西,其离开函数就会死亡了。

而上面函数内所创建的新的临时对象,是通过 typename() 的形式创建的,这种 temp object ,没有名称,只由 类型+() 组成,一旦函数运行结束便死亡了。如:

int(7);
...
return complex(real(x) + y, imag(x));

下面c1、c2不是临时对象,而 complex()complex(4, 5)cout << complex(2) 均是临时对象。

complex c1(2, 1);
complex c2;
complex();
complex(4, 5);
cout << complex(2);

对于特殊的操作符,重载函数应为 全局函数(global)

#include <iostream.h>
ostream& operator << (ostream& os, const complex& x){
	return os << '(' << real(x) << ',' << imag(x) << ')';
}
{
	complex c1(2, 1);
	cout << c1 << complex(2);
}

上面对于重载的<<,先看返回类型能不能用引用传递,因为 cout是已经存在的object ,所以返回值传递可以使用引用传递。再查手册知,cout是ostream类,且cout是会被修改的,所以参数传递时用 ostream& 而不是 const ostream&

同时返回值传递时不能在函数前加const限定 (即const ostream& operator <<(…)) ,且也不能设置函数类型为空 (即void operator <<(…)) ,因为 cout << c1 执行完后需要返回对应的ostream& ,然后才可以接收后续的 (结果)<< complex(2)

#include <iostream.h>
void operator << (ostream& os, const complex& x){
	os << '(' << real(x) << ',' << imag(x) << ')';
}
{
	complex c1(2, 1);
	cout << c1; 					可以正确运行
	cout << complex(2);				可以正确运行
	cout << c1 << complex(2);		错误!不可正确运行
}
③ 常量成员函数(const member functions)

class里面的有 会改变数据的不会改变数据的 两种函数,若为 不会改变数据的 则需 在函数的后头加const

double real() const {
	return re;
}

此时,以下两种创建对象的代码都可以正常运行:

{
complex c1(2, 1);
cout <<  c1.real();
}
{
const complex c1(2, 1);
cout <<  c1.real();
}

但若函数不加 const限定 ,而所创建对象又为常量对象,则编译器会报错:

double real() {
	return re;
}

此时,第一种创建对象代码可以正常运行,而第二种则编译器会报错:

{
complex c1(2, 1);
cout <<  c1.real();
}
{
const complex c1(2, 1);
cout <<  c1.real();
}
错误!
④ 参数传递(pass by value or reference(to const)

一般来说,引用传递值传递 ,因为引用传递相当于传递的是指针,操作效率更高(但不是说所有情况下都是 引用传比值传快 ,因为如果只是传一个1字节大小的字符,值传递此时只需传输一个字节,而引用传递则需像指针传递一样需要传输4字节)。所以一般情况下,均建议使用引用传递,提高代码效率

pass by reference
complex&
const complex&

引用传递因为像指针一样,若在操作中对引用传递的对象进行了修改,则相应的原来被引对象也会发生变化,若不想在传递过程中造成被引对象的变化,则需在加 关键字const 限定。

⑤ 返回值传递(return by value or reference(to const)
complex& operator += (const complex&)
double real() const{
	return re;
}
friend complex& __doapl(complex*, const complex&);
ostream& operator << (ostream& os, const complex& x){
	return os << '(' << real(x) << ',' << imag(x) << ')';
}

Ⅰ. 返回值引用传递时需要注意:
若函数内部处理后产生的结果,是放置在本来已经存在的空间上时,则可以使用返回值传递:

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

若函数的操作结果或运算结果,是放置在本来还未存在的需要在函数里面创建出来的空间上时,是个locall变量,其返回的结果会随着函数结束本体就消亡掉了,执行到代码语句的下一行就消失掉了,所以不可使用返回值传递:

inline complex complex::operator + (const complex& x, const complex& y){
	return (x.real() + y.real());
}

对比:

inline complex& operator + (const complex& x){
	return x;
}
inline complex operator - (const complex& x){
	return complex(-real(x), -imag(x));
}

前者可使用返回值引用传递,但后者不可。

Ⅱ. 传递者 无需知道 接收者 是以何种形式接收数据。

inline complex& __doapl(complex* ths, const complex& r){
	...
	return *ths;
}

上面代码中,*ths 是传递者,而 complex& 是接收者,即无论返回值是何种形式,都可用引用的方式接收返回值

inline complex& complex::operator += (const complex& r){
	return __doapl(this, r);
}

int main(){
	complex c1(2, 1);
	complex c2(5);
	c2 += c1;
}

类似的,c1 是传递者,而 const complex& r 是接收者,也是采取了引用传递的方式接收了返回值

Ⅲ. 连串使用时,需注意返回值类型不能空

c3 += c2 += c1;

此时,返回值类型即函数类型不能定义为void ,应定义为引用传递类型 complex&
因为其是相当于 c2 += c1运算的结果 ,再返回给 c3 进行操作即 c3 += (结果)

inline complex& complex::operator += (const complex& r){
	return __doapl(this, r);
}

若只是:

c2 += c1;

则函数类型可定义为 void ,不影响:

inline void complex::operator += (const complex& r){
	return __doapl(this, r);
}
⑥ 友元(friend)

class中的friend函数可以自由取得private中的数据

template <typename T>
class complex{														
public:																
	complex(T r = 0, T i = 0): re(r), im(i) {}
	complex& operator += (const complex&);
	T real() const{
		return re;
	}
	T imag() const{
		return im;
	}
private:
	T re, im;
	
	friend complex& __doaple(complex*, const complex&);
};

inline complex& __doapl(complex* ths, const complex& r){
	ths->re += r.re;
	ths->im += r.im;
	return *ths;
}

注意同个class内的各个objects互为friends(友元)

template <typename T>
class complex{														
public:																
	complex(T r = 0, T i = 0): re(r), im(i) {}
	complex& operator += (const complex&);
	T func(const complex& param){
		return param.re + param.im;
	}
private:
	T re, im;
};

int main(){
	complex<int> c1(2, 1);
	complex<int> c2;
	c2.func<int>(c1);
}

本文链接http://www.dzjqx.cn/news/show-617497.html