C++学习笔记:(四)运算符重载&类型转换

6.运算符重载

面向对象程序设计的重载有函数重载和运算符重载。函数重载是指在相同作用域内,若干个参数特征不同的函数使用相同的函数名,也称为函数名重载;运算符重载是另一种调用函数的方法,是指同样的运算符可以施加于不同类型的操作数上面,也就是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时产生不同的行为,是一种静态联编的多态。

 

6.1运算符重载的基本概念

与函数重载相似,运算符也存在重载问题。C++语言预定义的运算符只适用于基本数据类型。为了解决一些实际问题,程序员经常会定义一些新类型,即自定义类型,然而C++不允许生成新的运算符,因此为了实现对自定义类型的操作,就必须自己来编写函数说明某个运算符如何作用于这些数据类型,这样的程序可读性较差。针对这种情况,C++允许重载现有的大多数运算符,也就是允许给已有的运算符赋予新的含义,从而提高了C++的可扩展性,使得针对同样的操作,使用重载运算符比使用显式函数调用更能提高程序的可读性。

C++语言对运算符重载进行了以下规定限制:

(1)只能重载C++语言中原先已定义的运算符,不能自己创造新的运算符进行重载。

(2)并不是所有的运算符都可以重载。不能进行重载的运算符:. .* :: ?:

(3)不能改变运算符原有的优先级和结合性。

(4)不能改变运算符对预定义类型数据的操作方式,但是可以根据实际需要,对原有运算符进行适当的改造和扩充。

(5)运算符重载有两种方式:重载为类的成员函数&重载为类的友元函数。

 

6.2成员函数重载运算符

成员函数重载运算符的原型在类的内部

class 类名
{
    ...
    返回类型 operator 运算符(形参表)
};
在类外定义成员运算符函数的格式:
返回类型 类名::operator 运算符(形参表)
{
    //函数体
}

返回类型是指运算符重载函数的运算结果类型;operator是定义运算符重载函数的关键字;运算符是要重载的运算符名称;形参表中给出了重载运算符所需要的参数和类型。

 

6.2.1单目运算符重载

用成员函数重载运算符时,如果运算符是单目的,则参数表为空。因为这时操作数访问该重载运算符的对象本身的数据,该对象有this指针指向,所以参数表为空。

单目运算赋的调用有两种方式:显式调用和隐式调用。

(1)显式:对象名.operator 运算符()

(2)隐式:重载的运算符 对象名

重载”-”运算符:

#include <iostream>
#include <iomanip>
using namespace std;

class Point
{
	private:
		int x,y;
	public:
		Point(int i = 0, int j = 0);
		Point operator -();		
		void Print();

};

Point::Point(int i, int j)
{
	x = i;
	y = j;
}

void Point::Print()
{
	cout << "x=" << x << setw(5) << "y=" << y <<endl;
}

Point Point::operator -()
{
	x = -x;
	y = -y;
	return *this;
}

int main()
{
	Point ob1(1, 2);
	cout << "ob1:" <<endl;
	ob1.Print();
	cout << "-ob1:" <<endl;
	ob1.operator -();     //显式调用
	//ob = -ob1;          //隐式调用
	ob1.Print();
	return 0;
}

在进行重载时,函数没有接收参数,运算符后的操作数有this指针指出。语句x = -x; y = -y;相当于this->x = -this->x; this->y = -this->y;。

 

6.2.2双目运算符重载

重载双目运算符时,左边操作数是访问该重载运算符的对象本身的数据,有this指针指向,右边操作数通过成员运算符函数的参数指出。所以,此时成员运算符函数只有一个参数。

双目运算赋的调用有两种方式:显式调用和隐式调用。

(1)显式:对象名.operator 运算符()

(2)隐式:对象名 重载的运算符 参数

重载”+”运算符,实现两个字符串相加:

#include <iostream>
#include <string.h>
using namespace std;
const int Max = 20;
class String 
{

	private:
		char buffer[Max];
		int length;
	public:
		String(char *in_str);
		String();
		String operator +(char *append_str);
		void showstring();
};

String::String(char *in_str)
{
	strcpy(buffer, in_str);
	length = strlen(buffer);
}

String::String()
{
	length = 0;
}

String String::operator +(char *append_str)
{
	String temp;
	int templen;
	templen = strlen(buffer)+strlen(append_str)+1;
	if(templen > Max)
	{
		cout << "String is too large!" <<endl;
		strcpy(temp.buffer, buffer);
		return temp;
	}
	length = templen;
	strcpy(temp.buffer, buffer);
	strcat(temp.buffer, append_str);
	return temp;
}

void String::showstring()
{
	cout << buffer <<endl;
}

int main()
{
	String title((char*)"C/C++ ");
	title = title + (char*)"Program";             //隐式调用
	//title = title.operator+("Program");         //显式调用,不加char*会出现警告
	title.showstring();
	return 0;
}

在执行语句title = title + “Program”;时,编译系统首先将title+”Program”解释为title.operator+(“Program”),从而调用运算符重载函数operator+(char*append_str)实现字符串的相加。最后再将运算符重载函数的返回值赋给title。在进行重载时,函数只接受一个参数,该参数是运算符的第二个参数,第一个参数由this指针指向。语句strcpy(temp.buffer, buffer);相当于strcpy(temp.buffer, this->buffer);。

用成员函数重载运算符实现复数的加、减运算:

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r = 0.0, double i = 0.0);
		void Print();
		Complex operator+(Complex a);
		Complex operator-(Complex a);

};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex Complex::operator+(Complex a)
{
	Complex temp;
	temp.real = real + a.real;
	temp.imag = imag + a.imag;
	return temp;
}

Complex Complex::operator-(Complex a)
{
	Complex temp;
	temp.real = real - a.real;
	temp.imag = imag - a.imag;
	return temp;
}

void Complex::Print()
{
	cout <<real;
	if(imag > 0)
		cout << "+";
	if(imag != 0)
		cout << imag << "i" <<endl;
}

int main()
{
	Complex c1(1.1, 2.2), c2(3.3, 4.4),total;
	total = c1 + c2;
	total.Print();
	total = c1 - c2;
	total.Print();
	return 0;
}

 

6.2.3重载++、--运算符

现在C++能识别”++”、”--”运算符是前缀的还是后缀的,它们是单目运算符,分为前缀和后缀两种。

类名 operator ++()      //前缀方式

类名 operator ++(int)    //后缀方式

类名 operator --()       //前缀方式

类名 operator --(int)     //后缀方式

重载”++”&”--”运算符:

#include <iostream>
#include <iomanip>
using namespace std;

class Point
{
	private:
		int x,y;
	public:
		Point(int i = 0, int j = 0);
		Point operator++();
		Point operator--(); 
		Point operator++(int); 
		Point operator--(int); 		
		void Print();

};

Point::Point(int i, int j)
{
	x = i;
	y = j;
}

void Point::Print()
{
	cout << "(" << x << "," << y << ")" <<endl;
}

Point Point::operator--()
{
	--x;
	--y;
	return *this;
}
Point Point::operator++()
{
	++x;
	++y;
	return *this;
}
Point Point::operator--(int)
{
	Point temp = *this;
	x--;
	y--;
	return temp;
}
Point Point::operator++(int)
{
	Point temp = *this;
	x++;
	y++;
	return temp;
}

int main()
{
	Point ob1(1, 2), ob2(3, 4), ob;
	cout << "ob1:";
	ob1.Print();
	cout << "++ob1: ";
	++ob1;
	cout << "ob1:";
	ob1.Print();
	cout << "ob2:";
	ob2.Print();
	cout << "--ob2: ";
	--ob2;
	cout << "bo2: ";
	ob2.Print();
	cout << "ob = ob1++" <<endl;
	ob = ob1++;
	cout << "ob: ";
	ob.Print();
	cout << "ob1: ";
	ob1.Print();
	cout << "ob = ob1-- " <<endl; 
	ob = ob--;
	cout << "ob: ";
	ob.Print();
	cout << "ob1: ";
	ob1.Print();
	return 0;
}

 

6.2.4重载赋值运算符

C++中,对于任何一个类,如果没有用户自定义的赋值运算符函数,系统会自动地为其生成一个默认的赋值运算符函数,以完成数据成员之间的复制。通常情况下,默认的赋值运算符函数可以完成赋值任务,但在某些特殊情况下,如类中有指针的情况下,就不能进行直接赋值。

指针悬挂问题:

#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Student
{
	private:
		char *name;
		float score;
	public:
		Student(char *na, float sco);
		~Student();		
		void Print();
};

Student::Student(char *na, float sco)
{
	name = new char[strlen(na)+1];
	strcpy(name,  na);
	score = sco;
}

Student::~Student()
{
	delete name;
}

void Student::Print()
{
	cout << "student:" << name << "  " << "Score:" << score <<endl;
}

int main()
{
	Student p1((char*)"zhangming", 90.5);
	p1.Print();
	Student p2((char*)"wanglan", 95);
	p2.Print();
	p2 = p1;
	cout << "p2:";
	p2.Print();
	return 0;
}

程序运行结果出错的原因是当执行赋值语句p2 = p1时,这时p2和p1里的name指针指向了同一个空间,当对象p1和p2的生存期结束时,系统将调用析构函数释放空间,因为只有一个空间,所以只能释放一次,另一个指针所指的空间就不存在了,产生了指针悬挂。

解决:

#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Student
{
	private:
		char *name;
		float score;
	public:
		Student(char *na, float sco);
		Student &operator=(const Student &);		
		~Student();		
		void Print();
};

Student::Student(char *na, float sco)
{
	name = new char[strlen(na)+1];
	strcpy(name,  na);
	score = sco;
}

Student::~Student()
{
	delete name;
}

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

void Student::Print()
{
	cout << "student:" << name << "  " << "Score:" << score <<endl;
}

int main()
{
	Student p1((char*)"zhangming", 90.5);
	Student p2((char*)"wanglan", 95);
	cout << "p2:"	;
	p2.Print();
	p2 = p1;
	cout << "p2:";
	p2.Print();
	return 0;
}

注意:(1)赋值运算符不能重载为友元函数,只能重载为一个非静态成员函数。

(2)赋值运算符重载函数不能被继承。

 

重载”+=”运算符,实现的是字符串的连接运算:

#include <iostream>
#include <string.h>
using namespace std;

class String
{
	private:
		char *buffer;
		int length;
	public:
		String(char *in_str);
		const String &operator+=(const String &);
		void showstring();
		~String();
};

String::String(char *in_str)
{
	length = strlen(in_str);
	buffer = new char[length+1];
	strcpy(buffer, in_str);
}

const String & String::operator+=(const String &append_str)
{
	char *temp = buffer;
	length += append_str.length;
	buffer = new char[length+1];
	strcpy(buffer, temp);
	strcat(buffer, append_str.buffer);
	delete[]temp;
	return *this;
}

void String::showstring()
{
	cout << buffer <<endl;
}

String::~String()
{
	delete []buffer;
}

int main()
{
	String s1((char*)"happy"),s2((char*)" birthday");
	s1 += s2;
	s1.showstring();
	s1 += (char*)" to you";
	s1.showstring();
	return 0;
}

 

6.2.5重载下标运算符

当程序变得更为复杂时,有时必须重载数组下标运算符[],C++重载数组运算符时认为它是双目运算符,因此重载数组下标运算符[]时,运算符成员函数的形式:

返回类型  类名::operator[] (形参)
{
    //函数体
}

重载下标运算符,用一维数组实现一个三维向量类:

#include <iostream>
using namespace std;

class Vector
{
	private:
		int v[3];
	public:
		Vector(int a1, int a2, int a3);
		int &operator[](int bi);
};

Vector::Vector(int a1, int a2, int a3)
{
	v[0] = a1;
	v[1] = a2;
	v[2] = a3;
}

int & Vector::operator[](int bi)
{
	if(bi < 0 || bi >= 3)
	{
		cout << "Bad subscript!" <<endl;
		exit(1);
	}
	return v[bi];
}

int main()
{
	int i;
	Vector v(1, 3, 5);
	cout << "Befor:" <<endl;
	for(i = 0; i < 3; i++)
		cout << v[i] << "  ";
	cout <<endl;
	cout << "After:" <<endl;
	for(i = 0; i < 3; i++)
		v[i] = 3*i;
	for(i = 0; i < 3; i++)
		cout << v[i] <<"  ";
	cout <<endl;

	return 0;
}

程序执行语句cout << v[i] << setw(4)时,编译系统将v[i]解释为v.operator[](i),从而调用运算符重载函数operator [](int bi)。在函数中首先判断数组下标的值是否越界,如果越界,则显示相应的错误信息,否则返回下标所对应的元素的值。在定义重载[]函数时,由于返回是一个int的引用,因此可以使重载的[]用在赋值语句的左边,所以语句v[i] = 2*i是合法的,从而使程序更加灵活。

注意:(1)重载下标运算符[]的一个优点是,可以增加C++中数组检索的安全性。

(2)重载下标运算符[]时,返回一个int的引用,可使重载的[]用在赋值语句的左边,因而在main函数中,v[i]可以出现在赋值运算符的任何一边,使编程更灵活。

 

6.2.6重载函数调用运算符”()”

重载函数调用运算符()时,并不是创建新的调用函数的方法,而是创建了可传递任意数目参数的运算符函数,通常重载函数调用运算符()时,定义了传递给重载函数的参数。重载函数调用运算符()成员函数的形式:

返回类型  类名::operator()(形参)
{
    //函数体
}

重载函数调用运算符”()”:

#include <iostream>
using namespace std;

class Mat
{
	private:
		int *m;
		int row,col;
	public:
		Mat(int, int);
		int &operator()(int, int);
};

Mat::Mat(int r, int c)
{
	row = r;
	col = c;
	m = new int[row*col];
	for(int i = 0; i < row*col; i++)
	{
		*(m+i) = i;
	}
}

int &Mat::operator()(int r, int c)
{
	return (*(m+r*col+c));
}

int main()
{
	Mat aM(10, 10);
	cout << aM(3, 4) <<endl;
	aM(3, 4) = 35;
	cout << aM(3, 4) <<endl;
	return 0;
}

在执行语句cout << aM(3, 4) <<endl;时,编译系统将aM(3, 4)解释为aM.operator()(3, 4),从而调用运算符重载函数operatori()(int r, int c),然后返回矩阵第3行第4列的元素的值。语句aM(3, 4) = 35;修改矩阵第3行第4列的元素的值,之所以能够这样写,是因为函数operator()返回的是引用类型int&的缘故。

 

6.3友元函数重载运算符

大多数情况下用友元函数或用成员函数重载运算符,在功能上是没有差别的。用友元函数重载运算符时,因为友元运算符函数没有this指针,所以如果运算符时单目的,则参数表中有一个操作数,如果运算符时双目的,则参数表中有两个操作数。其形式:

friend <函数类型>operator<重载的运算符>(<形参>)          //单目运算符重载
{...}
friend <函数类型>operator<重载的运算符>(<形参1>,<形参2>)  //双目运算符重载
{...}

用友元函数重载运算符实现复数的加、减运算:

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r = 0.0, double i = 0.0);
		void Print();
		friend Complex operator+(Complex a, Complex b);
		friend Complex operator-(Complex a, Complex b);
};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex operator+(Complex a, Complex b)
{
	Complex temp;
	temp.real = b.real + a.real;
	temp.imag = b.imag + a.imag;
	return temp;
}

Complex operator-(Complex a, Complex b)
{
	Complex temp;
	temp.real = b.real - a.real;
	temp.imag = b.imag - a.imag;
	return temp;
}

void Complex::Print()
{
	cout <<real;
	if(imag > 0)
		cout << "+";
	if(imag != 0)
		cout << imag << "i" <<endl;
}

int main()
{
	Complex c1(1.1, 2.2), c2(3.3, 4.4),total;
	total = c1 + c2;
	total.Print();
	total = c1 - c2;
	total.Print();
	return 0;
}

重载单目运算符”-”:

#include <iostream>
using namespace std;

class Point
{
	private:
		int x,y;
	public:
		Point(int i = 0, int j = 0);
		friend Point operator-(Point ob);		
		void Print();

};

Point::Point(int i, int j)
{
	x = i;
	y = j;
}

void Point::Print()
{
	cout << "(" << x << "," << y << ")" <<endl;
}

Point operator-(Point ob)
{
	ob.x = -ob.x;
	ob.y = -ob.y;
	return ob;
}

int main()
{
	Point ob1(1, 2), ob2(3, 4), ob;
	cout << "ob1:";
	ob1.Print();
	cout << "-ob1: ";
	ob2 = -ob1; 
	ob2.Print();
	return 0;
}

重载”++”运算符:

#include <iostream>
using namespace std;

class Point
{
	private:
		int x,y;
	public:
		Point(int i = 0, int j = 0);
		friend Point operator++(Point ob);
		void Print();

};

Point::Point(int i, int j)
{
	x = i;
	y = j;
}

void Point::Print()
{
	cout << "(" << x << "," << y << ")" <<endl;
}

Point operator++(Point ob)
{
	++ob.x;
	++ob.y;
	return ob;
}

int main()
{
	Point ob1(1, 2), ob2;
	cout << "ob1:" <<endl;
	ob1.Print();
	ob2 = ++ob1;
	cout << "++ob1: " <<endl;
	ob1.Print();
	cout << "ob2:" <<endl;
	ob2.Print();
	return 0;
}

这个运行结果与所希望的运行结果不一致,产生不一致的原因在于友元函数没有this指针,所以不能引用this指针所指的对象。这个函数采用对象参数通过传值的方法传递参数,函数体内对ob的所有修改都不会传到函数体外。因此,对象x和对象y并未增加,所以没有输出所希望的结果。解决这个问题有两个方法:一是采用引用参数传递操作数;二是用成员运算符函数进行重载。

引用参数传递操作数:

#include <iostream>
using namespace std;

class Point
{
	private:
		int x,y;
	public:
		Point(int i = 0, int j = 0);
		friend Point operator++(Point &ob);
		void Print();

};

Point::Point(int i, int j)
{
	x = i;
	y = j;
}

void Point::Print()
{
	cout << "(" << x << "," << y << ")" <<endl;
}

Point operator++(Point &ob)
{
	++ob.x;
	++ob.y;
	return ob;
}

int main()
{
	Point ob1(1, 2), ob2;
	cout << "ob1:" <<endl;
	ob1.Print();
	ob2 = ++ob1;
	cout << "++ob1: " <<endl;
	ob1.Print();
	cout << "ob2:" <<endl;
	ob2.Print();
	return 0;
}

 

6.4成员函数重载运算符和友元函数重载运算符比较

在进行运算符重载时,既可以是成员函数重载也可以是友元函数重载。下面是成员函数重载运算符与友元函数重载运算符的比较

(1)双目运算符,成员函数重载运算符带有一个参数,而友元函数重载运算符带有两个参数;单目运算符,成员函数重载运算符不带参数,而友元函数重载运算符带有一个参数。

(2)双目运算符一般可以被重载为友元运算符或成员运算符函数,下面的情况必须使用友元函数。

例如:用成员函数重载运算符”+”。

Complex Complex::operator +(int a)
{
    Complex temp;
    temp.real = real + a;
    temp.imag = imag + a;
    return temp;
}

如果类Complex的对象com要做赋值运算和加法运算,下面的语句时正确的。

com = com + 10;

这是因为对象com是运算符”+”的做操作数,在调用重载运算符”+”的函数时,this指针指向com。因此语句temp.real = real + a;相当于temp.real = this->real + a;。而下面的语句就不正确了。

com = 10 + com;

这是因为左操作数是一个整数,而整数是一个内部数据类型,不能产生对成员运算符函数的调用。解决这类问题的方法是采用两个友元函数来重载运算符函数+,从而消除运算符+的左操作数是内部数据类型而带来的问题。

用友元函数重载运算符”+”:

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r = 0.0, double i = 0.0);
		void Print();
		friend Complex operator+(Complex a, int c);
		friend Complex operator+(int c, Complex a);
};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex operator+(Complex a, int c)
{
	Complex temp;
	temp.real = a.real + c;
	temp.imag = a.imag + c;
	return temp;
}

Complex operator+(int c, Complex a)
{
	Complex temp;
	temp.real = c + a.real;
	temp.imag = c + a.imag;
	return temp;
}

void Complex::Print()
{
	cout <<real;
	if(imag > 0)
		cout << "+";
	if(imag != 0)
		cout << imag << "i" <<endl;
}

int main()
{
	Complex com(1.1, 2.2);
	com = com + 10;
	cout << "com + 10" <<endl;
	com.Print();
	cout << "10 + com" <<endl;
	com = 10 + com;
	com.Print();	
	return 0;
}

一般而言,对于双目运算符,将它重载为友元运算符函数比重载为成员运算符函数便于使用。如果一个运算符的操作需要修改类对象的状态,建议使用成员运算符函数;如果运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则运算符必须用友元函数,而不能用成员函数。

对于单目运算符,建议选择成员函数;

对于运算符=、()、[],建议选择成员函数;

对于运算符+=、-=、/=、*=、&=、!=、~=、%=、>>=、<<=,建议重载为成员函数。

其他运算符,建议重载为友元函数。

 

6.5类型转换

大多数程序都可以处理各种数据类型的信息,有时所有操作会集中在同一类型。例如,整数加整数还是整数(结果不超出整数的表示范围)。但是,在很多情况下都需要将一种类型的数据转换为另一种类型的数据。例如,在进行赋值、计算、向函数传值及函数返回值等,都有可能发生这种情况。对于内部类型(基本类型、预定义类型),编译器知道如何进行类型转换。程序员也可以用强制类型转换实现内部类型的强制转换。

 

6.5.1系统预定义类型之间的转换

C++规定,当不同类型的数据进行运算时,需先将数据转换成同一类型,然后才可以进行运算。数据的类型转换可以通过两种转换形式完成:一种是隐式类型转换,另一种是显式类型转换。

隐式转换:

当执行赋值表达式V=E时,如果V和E的类型不一致,则将E先转换为V后再赋值。与C语言一样,C++中规定数据类型级别从高到低的次序是:double->float->long int->int->short、char。当两个操作数类型不一致时,运算之前将级别低的自动转换为级别高的,然后再进行运算。例如,当char或short类型数据与int类型数据进行运算时,把char或short类型数据转换成int类型数据。

显式转换:

①强制转换法:(类型名)表达式   (float)(5%2)

②函数法:类型名(表达式)   float (a+b);

以上介绍的是一般数据类型之间的转换。如果用户自定义类型,如何实现它们与其他数据类型的转换?编译器不知道怎样实现用户自定义类型与内部数据类型之间的转换。通常采用两种方法:构造函数实现类型转换和用类类型转换函数进行类型转换。

 

6.5.2用构造函数实现类型转换

用构造函数完成类型转换,类内至少定义一个只带一个参数的构造函数。这样,当进行类型转换时,系统会自动调用该构造函数,创建该类的一个临时对象,该对象由被转换的值初始化,从而实现类型转换。

将一个char型数据转换为string类型数据:

#include <iostream>
#include <string.h>
using namespace std;
class String 
{

	private:
		char *str;
		int length;
	public:
		String(char *in_str);
		~String();
		void showstring();
};

String::String(char *in_str)
{
	length = strlen(in_str);
	str = new char[length+1];
	strcpy(str, in_str);
}

String::~String()
{
	delete []str;
}

void String::showstring()
{
	cout << str <<endl;
}

int main()
{
	String s = (char*)"C/C++ Program";
	s.showstring();
	return 0;
}

语句String(char *s);声明了一个转换构造函数。该构造函数可以用来进行类型转换。main函数中,在执行语句String s = “C/C++ program”;时,编译系统首先调用构造函数建立包含C/C++ program的一个临时string对象,然后再将该临时string对象赋给对象s。使用这种转换构造函数意味着不用再为将字符串赋给string类对象提供重载的赋值操作符(因为基本运算符”=”不允许将一个char*字符串赋给一个string类对象)。任何只带一个参数(或其他参数都带有默认值)的构造函数都可以认为是一种转换构造函数。

 

预定义类型向自定义的类类型转换:

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex();
		Complex(int r);
		Complex(double r, double i);
		void Print();
		friend Complex operator+(Complex a, Complex b);
};

Complex::Complex()
{
	real = imag = 0;
}

Complex::Complex(int r)
{
	real = r;
	imag = 0;
}


Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex operator+(Complex a, Complex b)
{
	Complex temp;
	temp.real = a.real + b.real;
	temp.imag = a.imag + b.imag;
	return temp;
}

void Complex::Print()
{
	cout <<real;
	if(imag > 0)
		cout << "+";
	if(imag != 0)
		cout << imag << "i" <<endl;
}

int main()
{
	Complex com1(1.1, 2.2), com2, com3 = 10;
	cout << "com1:";
	com1.Print();
	com1 = 20 + com1;
	cout << "com1=20+com1:" <<endl;
	com1.Print();
	com2 = 10 + 200;
	cout << "com2=10+200:" <<endl;
	com2.Print();
	cout <<endl;
	cout << "com3=10:" <<endl;
	com3.Print();
	cout <<endl;
	return 0;
}

译程序在分析赋值表达式com3 = 10;时,根据隐式类型转换规则,将整数10转换为com3的类型,通过调用构造函数Complex(int r)完成所需要的类型转换。在分析赋值表达式com1 = 20 + com1时,编译程序首先调用构造函数Complex(int r)将整数20转换成Complex类型,然后再调用运算符函数operator+完成两个Complex类型数据的加法运算,最后再赋值。在赋值表达式com2 = 10 + 200时,编译系统首先完成整数的加法运算,然后再调用构造函数Complex(int r)将整型数210转换成Complex类型,最后再赋值。

这写这个代码的时候还犯了一个小错误(出现了重载构造函数模糊的情况):

原因是我开始写的两个构造函数是:

Complex();
Complex(double r = 0, double i = 0);
//Complex(double r, double i);         //解决问题

 

6.5.3用类类型转换函数进行类型转换

使用构造函数可以实现类型转换,但是其所完成的类型转换功能具有一定的局限性。由于无法为系统预定义类型定义构造函数,因此,不能利用构造函数把自定义类型的数据转换为系统预定义类型的数据,只能实现系统预定义类型向自定义的类类型转换。

为了解决上述问题,C++允许用户在源类中定义类类型转换函数,从而实现吧源类类型转换成目的类型。

类类型转换函数定义格式:

class 源类类名
{
    //...
operator 目的类型()
{
    //...
    return 目的类型的数据;
}
};

其中,源类类名为要转换的源类类型;目的类型为要转换成的类型,它既可以是用户自定义的类型,也可以是系统的预定义类型。

使用类类型转换函数时,需要注意:

(1)类类型转换函数只能定义为一个类的成员函数,而不能定义为类的友元函数。

(2)类类型转换函数既没有参数,也不显式给出返回类型。

(3)类类型函数中必须有return目的类型的数据;的语句,即必须返回目的类型数据作为函数的返回值。

自定义类型向预定义类型的转换(显式调用):

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r = 0, double i = 0);
		operator float();
		operator int();
		void Print();
};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
	cout << "Constructing..." <<endl;
}

Complex::operator float()
{
	cout << "Type changed to float" <<endl;
	return real;
}

Complex::operator int()
{
	cout << "Type changed to int" <<endl;
	return int(real);
}

void Complex::Print()
{
	cout << "(" <<real << "," << imag << ")" <<endl;
}

int main()
{
	Complex a(1.1, 2.2);
	a.Print();
	cout << float(a)*0.5 <<endl;
	Complex b(4.7, 6);
	b.Print();
	cout << int(b)*2 <<endl;
	return 0;
}

用隐式转换实现类类型转换:

#include <iostream>
using namespace std;

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r, double i);
		Complex(double i = 0);
		operator double();
		void Print();
};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex::Complex(double i)
{
	real = imag = i;
}

Complex::operator double()
{
	cout << "Type changed to double" <<endl;
	return real+imag;
}

void Complex::Print()
{
	cout << "(" <<real << "," << imag << ")" <<endl;
}

int main()
{
	Complex a(1.1, 2.2), b(2.3, 3.2), c;
	c = a + b;
	c.Print();	
	return 0;
}

本程序类Complex并没有重载运算符”+”,是如何实现”+”的运算的?这是由于C++可以自动进行隐式转换。在执行语句com = com1 + com2;时,首先寻找成员函数的+运算符,没有找到;寻找非成员函数的+运算符,也没有找到。由于系统中存在基本类型的+运算,因此寻找能将参数转换成基本类型的类型转换函数,结果找到operator double(),于是调用operator double()将com1和com2转换成了double类型,然后进行相加,由于最后要将结果赋给Complex类的对象,因此调用构造函数Complex(double i)将相加所得的结果转换成Complex类的一个临时对象,然后将其赋给Complex对象com。

用类类型转换函数实现复数类型向二维向量类型的转换:

#include <iostream>
using namespace std;

class Vector
{
	private:
		double x,y;	
	public:
		Vector(double tx = 0, double ty = 0);
		void print();
};

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r = 0, double i = 0);
		operator Vector();
};

Complex::Complex(double r, double i)
{
	real = r;
	imag = i;
}

Complex::operator Vector()
{
	return Vector(real, imag);
}

Vector::Vector(double tx, double ty)
{
	x = tx;
	y = ty;
}

void Vector::print()
{
	cout << "(" << x << "," << y << ")" <<endl; 
}

int main()
{
	Vector v;
	Complex a(1.1, 2.2);
	v = a;
	v.print();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38289815/article/details/81508465