C++學習筆記:(四)運算符重載 & 類型轉換

目錄

6.運算符重載

6.1運算符重載的基本概念

6.2成員函數重載運算符

6.3友元函數重載運算符

6.4成員函數重載運算符和友元函數重載運算符比較

6.5類型轉換


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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章