第十二天(使用類)

      這一章看似很長,其實有很大一部分內容沒記得必要:自定義的的Vector類,用來加深重載操作符的印象;類的轉換,不是java中的將子類轉換成父類,所得的對象應調用父類的方法還是子類的方法那麼複雜,而是將基本數據類型與類對象之間的轉換,這種轉換隻能應用於擁有一個參數的構造函數的類,這樣的轉換應用狹窄,且有一種並不複雜的方法替代。


2011-11-05(Working with Classes)
1、操作符重載(Operator Overloading)。
形式如下:
                     operator op(argumentList);
例:

#include <iostream>

class A
{
private:
    int AMember;
public:
    A(int AM = 5);
    A sum(A&);
    A operator +(A&);
    void show(){std::cout << AMember << " ";}
};

A::A(int AM ){AMember = AM;}

A A::sum(A& guest)
{
    A a;
    a.AMember = this->AMember + guest.AMember;
    return a;
}

A A::operator +(A& guest)
{
    A a;
    a.AMember = this->AMember + guest.AMember;
    return a;
}

int main()
{
    A a[3] = {A(10), A(20), A(30)};
    A result = A();

    result = a[0].sum(a[1]);result.show();
    result = a[1] + a[2];result.show();
    result = a[0].operator+(a[2]);
    result = a[0] + a[1] + a[2];result.show();
}
輸出: 30 50 40 60
①可看出,重載操作符和sum的函數體一樣,內部的實現還是一樣的,不同的是調用起來“+”比“sum”更直觀、更突出本質;
②Ob1 op Ob2等效於Ob1.opOverloadingFunc(Ob2),所以左右操作數不能隨便交換。根據這個可解釋35行;
③重載操作符的參數至多有一個,因爲重載的操作符是一元或是二元(不能重載?:操作符)。
2、重載操作符的限制。
①重載後操作符至少有一個操作數是用戶自定義類型。否則會產生歧義;
②不能違反語法規則(如不能將二元操作符重載成一元),不能修改優先級(重載時優先級怎麼修改?);
③不能自定義操作符,如不能
operator @(),因爲C++沒有@操作符;
④不能重載下列操作符:
sizeof, .(成員操作符), .*(成員指針操作符), ::, ?:, typeid, const_cast, dynamic_cast, reinterpret_cast, static_cast(後面幾個不懂,不解釋);
⑤下列操作符只能通過成員函數重載:
=, (), [], ->
3、友元(Friend)。下面的操作符重載:
A A::operator *(int mul)
{
    A a;
    a.a.AMember = this->AMember * mul;
    return a;
}
使用的時候,只能result = a * 5,而不能result = 5 * a。但是,根據我對數學的瞭解,這二者必然是一樣的。實際上,如果參數是兩個以上:
                                                   A operator *(int mul, A& a);
則可以將非A對象置於左邊。根據1③,這必須不是類的成員函數。然而,要想實現這個函數的功能,必須要訪問類的私有變量。矛盾情況下,誕生了友元函數(其實還有友元類、友元成員函數,不過要到後面才提)。這是一種能訪問某個類的私有變量的函數。函數的原型在類中加上關鍵字
friend聲明(原因是由類決定哪個函數是友元),其次,友元函數不是類的成員函數,所以定義之時不需加上作用域解析符:“::”。舉例如下:
class A
{
    //... ...
    friend A operator *(int,A&);
    //... ...
}
//... ...
A operator *(int mul,A& guest)
{
    A a;
    a.AMember = guest.AMember * mul;
    return a;
}
如此即可使用5 * a來使用重載操作符。但是這樣的友元函數的參數列表各參數的順序是一一對應的,即(int ad,A& guest)對應的是5 * a,而(A& guest, int ad)對應的是a * 5,所以想要實現交換律,必須要定義兩個友元函數(或者是一個友元函數,一個類的成員函數)。
**聰明的人可能已經想到了解決那個矛盾的方法: 
A operator *(int mul, A& guest)
{
     return guest * mul;
}
這雖然可以不要友元函數,但這一小點的主題是如何定義和使用友元函數,提之何用?
4、重載<<操作符。
重載<<操作符可替代上述類中show()成員函數,採用:

                         cout << aObject
的形式即可打印A類的對象的相關信息。下面來討論實現這個功能的相關問題:
①如果重載操作符函數是類的成員函數,則在使用這重載操作符的時只能採用如下形式:
                         aObject << cout
如此形式稍顯不妥,所以可使用友元函數;
②從前面知道,
cout是ostream的一個對象,所以重載函數的第一個參數即是ostream對象的引用,第二個參數即是A的對象的引用:
                         void operator <<(ostream&, A&);
如此,在使用cout << aObject的時候即是調用了:
                         operator <<(cout, aObject);
③上述的函數原型,有一個問題:
                         cout << aObject1 << aObject2; 
是不能工作的,因爲cout << aObject1返回的是
void,不能使用<<打印後面的aObject2。因此,重載的函數返回類型也必須是ostream的對象:
                         ostream& operator <<(ostream&, A&);
綜上,可實現如下:
class A
{
    //… …
    friend std::ostream& operator <<(std::ostream&, A&);
};

std::ostream& operator <<(std::ostream& os, A& a)
{
    os << "AMember = " << a.AMember << std::endl;
    return os;
}
5、產生僞隨機數。
C/C++庫中提供一個rand()
函數來生成0到RAND_MAX(=SHRT_MAX)的僞隨機數。如果想要範圍縮小,通常的做法是配合取模操作符來產生:
                      rand() % 100//create 0~100 random number
單單使用rand()函數,因爲其中的算法是一樣的,所以同一平臺每次運行的產生隨機數都是一樣的。所以rand()需要一個產生隨機數的種子(Seed),根據種子的不同可產生不同的隨機序列。設置隨機種子可使用
                      srand(unsigned seed);
然而,如果程序都固定設置一個種子,如:
unsigned int seed = 15;
srand(seed);
std::cout << rand();
每次運行的結果依然一樣。是什麼在每次運行程序的時候都不一樣?是時間。所以通常的做法是用當前時間作爲種子,使僞隨機更像隨機:
                     
srand((unsigned)time(0));
time(0)返回的即是當前時間。
**需要頭文件
cstdlib和ctime來支持srand, rand和time


再來個課後習題:

定義複數類,重載+, -, *, ~, <<, >>操作符

//fileName:complex0.h--the declaration of class
#ifndef COMPLEXX_H_
#define	COMPLEXX_H_

#include <iostream>

using std::istream;
using std::ostream;

class complex
{
private:
	double realPart;
	double imagPart;
public:
	complex(double rP = 0, double iP = 0);
	~complex();
	complex operator +(const complex&)const;
	complex operator -(const complex&)const;
	complex operator *(const complex&)const;
	complex operator ~();

	friend complex operator *(double,const complex&);
	friend ostream& operator <<(ostream&,const complex&);
	friend istream& operator >>(istream&,complex&);
};
#endif

//fileName:complex0.cpp--the implementation of class
#include "complex0.h"

using std::cout;
using std::cin;

complex::complex(double rP, double iP)
{
	realPart = rP;
	imagPart = iP;
}

complex::~complex(){}

complex complex::operator +(const complex& im)const
{
	return complex(realPart + im.realPart, imagPart + im.imagPart);
}

complex complex::operator -(const complex& im)const
{
	return complex(realPart - im.realPart, imagPart - im.imagPart);
}

complex complex::operator *(const  complex& im)const
{
	return complex(realPart * im.realPart, imagPart * im.imagPart);
}

complex complex::operator~()
{
	return complex(-realPart, -imagPart);
}
//define friend function
complex operator *(double fact,const complex& im)
{
	return complex(fact * im.realPart, fact * im.imagPart);
}

ostream& operator <<(ostream& os,const complex& im)
{
	os << "(" << im.realPart << ", " << im.imagPart << "i)";
	return os;
}

istream& operator >>(istream& is, complex& im)
{
	cout << "real: ";
	if(is >> im.realPart)
	{
		cout << "imaginary: ";
		is >> im.imagPart;
	}
	return is;
}
//fileName:Test.cpp--the test of class
#include <iostream>
using namespace std;
#include "complex0.h"  // to avoid confusion with complex.h

int main()
{
	complex a(3.0, 4.0);   // initialize to (3,4i)
	complex c;
	cout << "Enter a complex number (q to quit):\n";
	while (cin >> c)
	{
		cout << "c is " << c << "\n";
		cout << "complex conjugate is " << ~c << "\n";
		cout << "a is " << a << "\n";
		cout << "a + c is " << a + c << "\n";
		cout << "a - c is " << a - c << "\n";
		cout << "a * c is " << a * c << "\n";
		cout << "2 * c is " << 2 * c << "\n";
		cout << "Enter a complex number (q to quit):\n";
	}
	cout << "Done!\n";
	return 0;
}


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