《C++面向对象程序设计》课程笔记 lessen4

1. 运算符重载的基本概念

运算符重载的形式:

  • 运算符重载的实质是函数重载。
  • 可以重载为普通函数,也可以重载为成员函数。
  • 把含运算符的表达式转换为对运算符函数的调用。
  • 把运算符的操作数转换为运算符函数的参数。
  • 运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。 
  返回值类型 operator 运算符 (形参表)
  {
     ...
  }

运算符重载的实例:

#include <iostream>
using namespace std;

class Complex
{
public:
	double real,imag;
	Complex(double r=0.0,double i=0.0):real(r),imag(i){ }
	Complex operator - (const Complex & c);
};

Complex operator +(const Complex & a,const Complex & b)
{
	return Complex (a.real+b.real,a.imag+b.imag);  //因为没有对象名,所以返回的是一个临时对象
}

Complex Complex::operator-(const Complex & c)
{
	return Complex(real-c.real,imag-c.imag);     //因为没有对象名,所以返回的是一个临时对象
}

int main()
{
	Complex a(4,4),b(1,1),c;
	c = a + b;   //等价于 c = operator+(a,b);
	cout<<c.real<<","<<c.imag<<endl;
	cout<<(a-b).real<<","<<(a-b).imag<<endl;
	// a-b 等价于 a.operator - (b)

	system("pause");
	return 0;
}
  • 重载为成员函数时,参数个数为运算符操作数个数减一。
  • 重载为普通函数时,参数个数为运算符操作数个数。
  •  c = a + b; 等价于 c = operator + (a,b);
  • a - b; 等价于 a.operator - (b) 

2. 赋值运算符 '=' 的重载

  • 赋值运算符 '=' 只能重载为成员函数
#include <iostream>
using namespace std;

class String
{
private:
	char * str;
public:
	String():str(new char[1]) {str[0] = 0;}
	const char * c_str() { return str; }
	String & operator = (const char * s);
	~String() { delete [] str;}
};

String & String::operator= (const char * s)
{
	//重载“=”以使得 obj = "hello" 能够成立
	delete [] str;
	str = new char[strlen(s)+1];
	strcpy(str, s);
	return * this;
}

int main()
{
	String s;
	s = "Good Luck,";      //等价于 s.oprator = ("Good Luck,");
	cout<<s.c_str()<<endl;
	//	String s2 = "hello";   //这条是初始化语句而不是赋值语句,不会调用重载的赋值运算符。编译出错
	s = "Shenzhou 8!";     //等价于 s.oprator = ("Shenzhou 8!");
	cout<<s.c_str()<<endl;

	system("pause");
	return 0;
}

 当使用上面的类执行下面语句时

String s1,s2;
	s1 = "this";
	s2 = "that";
	s1 = s2;

效果形如:(调用默认的复制构造函数,s1与s2的每个字节都相同)

 造成的影响:

  • "this" 所在的空间被舍弃,造成空间浪费。
  • 如果 s1 对象消亡,析构函数将释放 s1.str 指向的地方。而当 s2 对象消亡时,将再一次释放。而 new 出来的只能被释放一次。 
  • 如果执行 s1 = "other" ; 会导致 s2.str 所指向的地方被 delete 。

因此要在 class String 里添加成员函数:

String & String::operator = (const String & s)
{
    if (this == & s)
	{
		return * this;
	}
	delete [] str;
	str = new char[strlen(s.str)+1];
	strcpy( str, s.str);
	return * this;
}

3. 可变长数组类的实现

#include <iostream>
using namespace std;

class CArray
{
	int size; //数组元素的个数
	int * ptr;//指向动态分配的数组
public:
	CArray(int s = 0); // s 代表数组元素的个数
	CArray(CArray & a);
	~CArray();
	void push_back(int v); //用于在数组尾部添加一个元素 v 。
	CArray & operator = (const CArray & a); //用于数组对象间的赋值
	int length(){ return size ;} //返回数组元素个数
	int & CArray::operator [](int i) //返回值为 int 不行,
    //( n = a[i] 可以,而 a[i] = 4 出错,非引用返回值不能作为赋值语句的左值)
	{
		//用以支持根据下标访问数组元素。如n = a[i] 和 a[i] = 4 这样的语句
		return ptr[i];
	}
};

CArray::CArray(int s):size(s)
{
	if (s==0)
	{
		ptr = NULL;
	}
	else
	{
		ptr = new int[s];
	}
}

CArray::CArray(CArray & a)
{
	if (!a.ptr)
	{
		ptr = NULL;
		size = 0;
		return;
	}
	ptr = new int[a.size];
	memcpy(ptr,a.ptr,sizeof(int)*a.size);
	size = a.size;
}

CArray::~CArray()
{
	if (ptr)
	{
		delete [] ptr;
	}
}

CArray & CArray::operator=(const CArray & a)
{   //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的一样
	if (ptr == a.ptr)   //防止 a = a 这样的赋值导致出错
	{
		return *this;
	}
	if (a.ptr == NULL)  //如果 a 里面的数组是空的
	{
		if (ptr)
		{
			delete [] ptr;
		}
		ptr = NULL;
		size = 0;
		return *this;
	}
	if (size<a.size) //如果原有空间够大,就不用分配新的空间
	{
		if (ptr)
		{
			delete [] ptr;
		}
		ptr = new int[a.size];
	}
	memcpy(ptr,a.ptr,sizeof(int)*a.size);
	size = a.size;
	return *this;
}

void CArray::push_back(int v) //在数组尾部添加一个元素
{
	if (ptr)
	{
		int * tmpptr = new int[size+1];//重新分配空间
		memcpy(tmpptr,ptr,sizeof(int)*size);//拷贝原数组内容
		delete [] ptr;
		ptr = tmpptr;
	}
	else   //数组本来是空的
		ptr = new int[1];
	ptr[size++] = v; //加入新的数组元素
}

int main()
{
	CArray a; //开始里的数组是空的
	for(int i=0;i<5;i++) //要用动态分配的内存来存放数组元素,需要一个指针成员变量
	{ 
		a.push_back(i);
	}
	CArray a2,a3;
	a2 = a;         //需要重载赋值运算符“=”
	for(int i=0;i<a.length();++i)
	{
		cout<<a2[i]<<" ";   // a2 是一个对象,但能像数组一样使用,需要重载“[]”运算符
	}
	a2 = a3; // a2 是空的
	for(int i=0;i<a2.length();++i) // a2.length() 返回0
	{
		cout<<a2[i]<<" ";
	}
	cout<<endl;
	a[3] = 100;
	CArray a4(a);  //初始化语句,需要自己写复制构造函数
	for(int i=0;i<a4.length();++i)
	{
		cout<<a4[i]<<" ";
	}

	system("pause");
	return 0;
}

4. 流插入运算符和流提取运算符的重载

1 流插入运算符的重载

  • cout 是 iostream 中定义的 ostream 类的对象
  • “ << ” 能用在 cout 上是因为在 iostream 里对 “ << ” 进行了重载。

单独实现 cout<< 5 和 cout<< "this" 的重载: 

void ostream::operator <<(int n)
{
	...    //输出 n 的语句
	return;
}

void ostream::operator <<(char * s)
{
	...    //输出 s 的语句
	return;
}

实现 cout<< 5 << "this" 的重载 :(想要连续执行,返回值应该为 ostream 类的对象(例:cout))

ostream & ostream::operator <<(int n)  //返回类型为 ostream 类的引用
{
	...    //输出 n 的语句
	return * this;  // * this 即 cout 对象,即返回 cout 对象的引用
}

ostream & ostream::operator <<(char * s)
{
	...    //输出 s 的语句
	return * this;
}
  • cout<< 5 << "this" 的函数调用形式是:cout.operator <<(5) .operator <<("this")
class CStudent
{
public:
	int nAge;
};

ostream & operator <<( ostream & o, CStudent & s)
{
	o<<s.nAge;
	return o;
}

int main()
{
	CStudent s;
	s.nAge = 5;
	cout<< s << "hello";

	system("pause");
	return 0;
}
  •  上面的程序将 "<<" 重载为全局函数,参数与运算符操作数个数相同。
  • 输出为 5hello 。(需要连续执行 “<<” 操作,返回值应是 ostream 类的引用)

例题:假定 c 是 Complex 类的对象,"cin>>c" 从键盘接受形式为 “a+bi” 的输入,“cout<<c” 以 “a+bi” 的形式输出 c 的值。

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Complex
{
	double real,imag;
public:
	Complex(double r=0,double i=0):real(r),imag(i){ };
	friend ostream & operator <<(ostream & os, const Complex & c); //因为要访问类的私有成员,声明为类的友元函数。
	friend istream & operator >>(istream & is, Complex & c);
};

ostream & operator <<(ostream & os, const Complex & c)
{
	os<<c.real<<"+"<<c.imag<<"i";//以 a+bi 的形式输出
	return os;
}

istream & operator >>(istream & is, Complex & c)
{
	string s;
	is>>s;        //将“a+bi”作为字符串读入
	int pos = s.find("+",0);
	string sTmp = s.substr(0,pos); //分离出代表实部的字符串
	c.real = atof(sTmp.c_str());  //atof 库函数能将 const char* 指针指向的内容转换成 float
	sTmp = s.substr(pos+1,s.length()-pos-2); //分离出代表虚部的字符串
	c.imag = atof((sTmp.c_str()));
	return is;
}

int main()
{
	Complex c;
	int n;
	cin>>c>>n;
	cout<<c<<","<<n;

	system("pause");
	return 0;
}

5. 类型转换运算符的重载

#include <iostream>
using namespace std;

class Complex
{
	double real,imag;
public:
	Complex (double r=0,double i=0):real(r),imag(i) { };
	operator double (){return real;}
	//重载强制类型转换运算符 double (返回值即为 double,所以不用指定返回值)。
};

int main()
{
	Complex c(1.2,3.4);
	cout<<(double) c<<endl; //输出1.2
	double n = 2 + (double)c;  //等价于 double n = 2 + c.operator double();
	cout<<n<<endl; //输出3.2

	system("pause");
	return 0;
}

6. 自增、自减运算符的重载

  • 前置运算符作为一元运算符重载

         重载为成员函数: 

T & operator ++();
T & operator --();

         重载为全局函数:

T1 & operator ++(T2);
T1 & operator --(T2);
  • 后置运算符作为二元运算符重载,多写一个没用的参数。(主要用于区分前置和后置)

        重载为成员函数: 

T & operator ++(int);
T & operator --(int);

         重载为全局函数:

T1 & operator ++(T2,int);
T1 & operator --(T2,int);
  • 自增、自减运算符重载的实例: 
#include <iostream>
using namespace std;

class CDemo
{
private:
	int n;
public:
	CDemo(int i=0):n(i) { }
	CDemo & operator ++();  //用于前置形式
	CDemo operator ++(int); //用于后置形式  //自增运算符声明为成员函数
	operator int () { return n;} //重载类型转换运算符。用于输出 cout<< CDemo对象。
	friend CDemo & operator -- (CDemo &);
	friend CDemo operator --(CDemo &, int);  //自减运算符声明为友元全局函数
	//原生态的 ++a 返回值为对象 a 的引用,(++a) = 1; //ok
	//  a++ 返回值为对象 a ,(a++) = 1; //error 
};

CDemo & CDemo::operator++()
{//前置++
	++n;
	return * this;
	// ++s即为:s.operator++();
}

CDemo CDemo::operator++(int k)
{//后置++
	CDemo tmp(* this);//记录修改前的对象
	n ++;
	return tmp; //返回修改前的对象
} // s++即为:s.operator++(0)

CDemo & operator --(CDemo & d)
{
	//前置 --
	d.n --;
	return d;
}//--s即为:operator--(s);

CDemo operator --(CDemo & d, int)
{
	//后置--
	CDemo tmp(d);
	d.n --;
	return tmp;
}//s--即为:operator--(s,0);

int main()
{
	CDemo d(5);
	cout<< (d++) <<",";  //等价于 d.operator ++(0);
	cout<< d <<",";
	cout<< (++d) <<",";  //等价于 d.operator ++();
	cout<< d <<endl;
	cout<< (d--) <<",";  //等价于 operator --(d,0);
	cout << d <<",";
	cout << (--d) <<","; //等价于 operator --(d);
	cout << d << endl;

	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sinat_35483329/article/details/84969755