1. 程式人生 > >C++Primer PLus 第五版讀書筆記

C++Primer PLus 第五版讀書筆記

處理第一個問題:
1)某書店以檔案形式儲存其每一筆交易。沒一筆交易記錄某本書的銷售情況,含有ISBM、銷售冊數和銷售單
價。每一筆交易形如:0-201-70352-X 4 24.99

-------------------------------------------------------------------


指標和const限定符
1)指向const物件的指標
const double *cptr
這裡的cptr是一個指向double型別const物件的指標,const限定了cptr指標所指向的物件型別,而並非cptr本身。也就是說,cptr本身並不是const。在定義時不需要對它進行初始化。如果需要的話,允許給cptr重新賦值,使其指向另一個const物件。但不能通過cptr修改其所指物件的值。
不能給一個常量賦值,常量指標所指的物件都不能修改:

#include "stdafx.h"
#include "iostream"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	double dbValue = 4;
	const double* cptr = &dbValue;
	*cptr = 5;
	return 0;
}


2)const指標
int iValue = 1;
int *const pNumber = &iValue;
此時,可以從右向左把這個定義語句讀作"pNumber"是指向int型物件的const指標。與其他const變數一樣,const指標的值也不能修改,這就意味著不能使pNumber指向其他物件。任何企圖給const指標賦值的行為(即使pNumber賦回同樣的值)都會導致編譯錯誤。
pNumber = pNumber;
與任何const量一樣,const指標也必須在定義的時候進行初始化。
指標本身是const的事實並沒有說明是否能使用該指標修改它所指向物件的值。指標所指物件的值能否修改完全取決於該物件的型別。

#include "stdafx.h"
#include "iostream"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int iValue = 1;
	int *const pNumber = &iValue;
	*pNumber = 2;
	return 0;
}

3)指向const物件的const指標
還可以如下定義指向const物件的const指標:
const double pNumble = 3.14;
const double *const pi_ptr = π
這時,既不能修改pi_ptr所指向物件的值,也不允許修改該指標的指向(即pi_ptr中存放的地址值)。可從右向左閱讀上述宣告語句:pi_ptr首先是一個const指標,指向double型別的const物件。

4)理解複雜的const型別的宣告
閱讀const宣告語句產生的部分問題,源於const限定符既可以放在型別前也可以放在型別後:
string const s1;
const string s2;
用typedef寫const型別定義時,const限定符載入型別名前面容易引起對所定義的真正型別的誤解:
string s;
typedef string* pString;
const pString cstr1 = &s;
pString const cstr2 = &s;
string* const cstr3 = &s;
把const放在型別pString之後,然後從右向左閱讀該宣告語句就會非常清楚地知道cstr2是const pString型別,即指向string物件的const指標


字串
1)C語言風格的字串:char* cp1;
此時一般用指標的演算法操作來遍歷C風格只付出,每次對指標進行測試並遞增1,直到到達結束符null為止

2)C風格字串的標準庫函式
C的標頭檔案:<string.h>
C++的標頭檔案:<cstring>

3)操作C風格字串的標準庫函式
strlen(str);-----------------------返回s的長度,不包括字串結束符null
strcmp(str1, str2);----------------比較兩個字串
strcat(str1, str2);----------------將字串str2連線到str1後,並返回str1
strcpy(str1, str2);----------------將str2複製給str1,並返回str1
strncat(str1, str2, n);------------將str2的前n個字元連線到到str1的後面,並返回str1
strncpy(str1, str2, n);------------將str2的前n個字元複製給str1,並返回str1

4)儘可能使用標準庫型別string
上面的C風格字串,不小心的使用都無可避免的出現記憶體管理問題,如果使用C++的標準庫型別string,則不存在上述問題,此時標準庫負責處理所有的記憶體管理問題,不必再擔心每一次修改字串時涉及到的大小問題。


寫入到檔案當中
使用ofstream寫入到檔案中,就像使用cout類似
使用檔案輸出的步驟:
1)包含標頭檔案fstream
2)建立ofstream物件outFile
3)outFile.open()
4)outFile << (就像使用cout那樣)

#include "stdafx.h"
#include "iostream"
using namespace std;
#include "fstream"


int _tmain(int argc, _TCHAR* argv[])
{
	char automoblie[20];
	int year;
	double a_price;
	double d_price;

	ofstream outFile;
	outFile.open("test.txt");
	
	cout << "Enter the make and model of automobile:" << endl;
	cin.getline( automoblie, 20 );
	cout << "Enter the model year:" << endl;
	cin >> year;
	cout << "Enter the original asking price:" << endl;
	cin >> a_price;
	d_price = 0.96 * a_price;
	
	cout << fixed;		 // floating point numbers using a general way the output
	cout.precision( 2 ); // output decimal point behind 1 bits
	cout.setf( ios_base::showpoint ); // compulsory output decimal point
	cout << "make and model of automobile is:" << automoblie << endl;
	cout << "model year is:" << year << endl;
	cout << "was asking:" << a_price << endl;
	cout << "now asking:" << d_price << endl;

	outFile << fixed;
	outFile.precision( 2 );
	outFile.setf( ios_base::showpoint );
	outFile << "make and model of automobile is:" << automoblie << endl;
	outFile << "model year is:" << year << endl;
	outFile << "was asking:" << a_price << endl;
	outFile << "now asking:" << d_price << endl;

	outFile.close();

	return 0;
}


讀取檔案
檔案輸出和檔案輸入極其類似
使用檔案輸出的步驟:
1)包含標頭檔案fstream
2)建立ifstream物件inFile
3)inFile.is_open()
4)getline( inFile, strOut );

#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
#include "algorithm"
#include "fstream"
#include "vector"

#define FILENAMELENGTH 20

void Print( const string str)
{
	cout << str << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	vector<string> vecStr;
	char strFileName[ FILENAMELENGTH ];
	string strOut;
	cout << "Enter the data file:" << endl;
	cin >> strFileName;

	ifstream inFile;
	inFile.open( strFileName );
	if ( !inFile.is_open() )
	{
		cout << "could not open the datafile" << strFileName << endl;
		cout << "Program terinating.\n";
		exit( EXIT_FAILURE );
	}
	while ( inFile.good() )
	{
		getline( inFile, strOut );
		vecStr.push_back( strOut );
	}

	for_each( vecStr.begin(), vecStr.end(), Print );

	inFile.close();


	return 0;
}


如果試圖開啟一個不存在的檔案,這將導致後面使用ifstream物件進行輸入時失敗。檢查檔案是否被成功開啟的首先方法是使用方法is_open(),為此,可以用類似的程式碼:

inFile.open( strFileName );
if ( !inFile.is_open() )
{
	cout << "could not open the datafile" << strFileName << endl;
	cout << "Program terinating.\n";
	exit( EXIT_FAILURE );
}


    如果檔案被成功地開啟,方法is_open()將返回true;因此如果檔案沒有被開啟,表示式!inFile.is_open()將為true。函式exit()的原型在標頭檔案cstdlib中定義,在該標頭檔案中,還定義了一個用於同作業系統通訊的引數值EXIT_FAILURE。函式exit()終止程式。
    使用good()方法,該方法在沒有發生任何錯誤時返回true

	while ( inFile.good() )
	{
		getline( inFile, strOut );
		vecStr.push_back( strOut );
	}


如果願意,可以使用其它方法來確定迴圈種終止的真正原因:

陣列作為函式引數
    當陣列作為函式引數時,將陣列的地址作為引數可以節省複製整個陣列所需的時機和記憶體
驗證一下,函式中陣列的地址與外層呼叫時,函式的地址是一樣的,並且在函式中

#include "stdafx.h"
#include "iostream"
using namespace std;

int sumArray( const int arr[], int nValue )
{
	int sum = 0;
	int nArrsize = 0;
	cout << "In sumArray function arr[] address:" << arr << endl;
	nArrsize = sizeof arr;
	cout << "In sumArray function sizeof arr is:" << nArrsize << endl;
	for ( int i = 0; i < nValue; i++ )
	{
		sum += arr[i];
	}
	return sum;
}
/*
int sumArray( const int* arr, int nValue )
{
	int sum = 0;
	int nArrsize = 0;
	cout << "In sumArray function arr[] address:" << arr << endl;
	nArrsize = sizeof arr;
	cout << "In sumArray function sizeof arr is:" << nArrsize << endl;
	for ( int i = 0; i < nValue; i++ )
	{
		sum += arr[i];
	}
	return sum;
}*/
int _tmain(int argc, _TCHAR* argv[])
{
	int nArrsize = 0;
	int arr[ 5 ] = { 1, 2, 3, 4, 5 };
	cout << "In _tmain function arr[] address:" << arr << endl;
	nArrsize = sizeof arr;
	cout << "In _tmain function sizeof arr is:" << nArrsize << endl;

	cout << "sum is:" << sumArray( arr, 5 ) << endl;

	return 0;
}


指標和const
將const用於指標有一些很微妙的地方,可以用兩種不同的方式將const關鍵字用於指標。
1)
第一種方法是讓指標指向一個常量物件,這樣可以防止使用該指標來修改所指向的值。

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	const int iValue = 10;
	int* pIValue = &iValue;

	*pIValue = 11;

	return 0;
}


2)
第二種方法是將指標本身宣告為常量,這樣可以防止改變指標指向的位置。

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int iValue = 10;
	const int* pIValue = &iValue;

	*pIValue = 11;

	return 0;
}


3)有這種情況:

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int iAge = 39;
	int* pIAge = &iAge;
	const int* pIValue = pIAge;
	*pIValue = 10;

	return 0;
}


這是情況變得很複雜。假如涉及的是一級間接關係,則將非const指標賦給const指標時可以的。
不過,進入兩級間接關係時,與一級間接關係一樣將const和非const混合的指標賦值方式將不再安全。如果允許這樣做,則可以編寫這樣的程式碼:
const int** ppIValue;
int* pIValue;
const int n = 13;
ppIValue = &pIValue;
*ppIValue = &n;
*pIValue = 10;
上述程式碼將非const地址(&pIValue)賦給了const指標(ppIValue),因此可以使用pIValue來修改const資料。所以,僅當只有一層間接關係(如指標指向基本資料型別)時,才可以將非const地址或指標賦給const指標。

4)
int IValue = 10;
const int* pIValue = &IValue;
這時能夠防止pIValue修改IValue的值,但是卻不能防止修改pIValue所指向的值。執行
int IAge = 39;
pIValue = &IAge;
是可行的

如果修改成
int* const pIValue = &IValue;
此時便不能修改pIValue所指向的值了。
在這個宣告中,關鍵字const的位置與以前不同。這種宣告格式使得pIValue只能指向IValue,但允許使用pIValue來修改IValue的值。
5)指向常量的常指標

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int IValue = 10;
	const int* const pIValue = &IValue;
//	*pIValue = 11;
	int IAge = 39;
	pIValue = &IAge;

	return 0;
}

這時既不能修改IValue的值,也不能修改pIValue的指向

函式和二維陣列
二維陣列在函式引數的形式
1)既可以是int sumArray( int arr[][ 4 ], int nSize )
由於指標型別指定了列數,因此sum()函式只能接受由4列組成的陣列。
原文中202頁31行“但長度變數指定了行數,因此sum()對陣列的行數沒有限制”這話說得有點讓人無法理解。其實應該是這樣的“行數需要由長度變數指定,因此sum()的陣列引數對於行數沒有限制”。

2)或者是int sumArray( int (*arr)[ 4 ], int nSize )
其中(*arr)中的括號是必不可少的,因為宣告int *arr[ 4 ]將宣告一個有4個指向int的指標組成的陣列,而不是一個指向由4個int組成的陣列的指標。另外函式引數不能是陣列。

3)二維函式的相關元素
arr2[ r ][ c ] = *( *( ar2 + r ) + c);
arr2    // 指向二維陣列的首地址
arr2 + r  // 指向二維陣列偏移r行的首地址
*(arr2 + r)  // 相當於arr2[ r ]
*(arr2 + r) + c  // 相當於arr2[ r ] + c
*( *(arr2 + r) + c ) // 相當於arr2[ r ][ c ]

#include "stdafx.h"
#include "iostream"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int arr2[ 2 ][ 4 ] = {
							{ 1, 2, 3, 4 },
							{ 5, 6, 7, 8 }
						  };
	cout << arr2[1][2] << endl;
	cout << arr2 << endl;
	int r = 1;
	cout << arr2 + r << endl;
	int c = 2;
	cout << *( arr2 + r ) << endl;
	cout << *( arr2 + r ) + c << endl;
	cout << *( ( arr2 + r) + c ) << endl;
	cout << *(*( arr2 + r ) + c ) << endl;
	
	return 0;
}


遞迴
1)
僅包含一個遞迴的呼叫

#include "stdafx.h"
#include "iostream"
using namespace std;

// recutsion with iValue
int sum( int iValue )
{
	if ( 0 == iValue )
	{
		return 1;
	}
	else
	{
		return sum( iValue - 1 ) * iValue;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << " 5 recursion:" << sum( 5 ) << endl;

	return 0;
}


此程式碼起警示作用:

#include "stdafx.h"
#include "iostream"
using namespace std;

// recutsion with iValue
int sum( int iValue )
{
	if ( 0 == iValue )
	{
		return 1;
	}
	else
	{
		return sum( iValue-- ) * iValue;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << " 5 recursion:" << sum( 5 ) << endl;

	return 0;
}



2)
包含多個遞迴呼叫的遞迴,在需要將一項工作不斷分為兩項較小的、類似的工作時,遞迴非常有用。

#include "stdafx.h"
#include "iostream"
using namespace std;

const int Len = 66;
const int Divs = 6;

void subdivide( char arr[], int low, int high, int level )
{
	if ( 0 == level )
	{
		return;
	}
	int mid = ( low + high ) / 2;
	arr[ mid ] = '|';
	subdivide( arr, low, mid, level - 1);
	subdivide( arr, mid, high, level - 1);
}

int _tmain(int argc, _TCHAR* argv[])
{
	char ruler[ Len ] = { 0 };
	int i;
	// initialize
	for ( i = 1; i <= Len - 2; i++ )
	{
		ruler[ i ] = ' ';
	}
	ruler[ Len - 1 ] = '\0';	// present end
	int iMax = Len - 2;			// 64min length is Len - 2
	int iMin = 0;				// set min length is 0
	ruler[ iMin ] = ruler[ iMax ] = '|'; // min and max pos now is '|'
	cout << ruler << endl;	// output none but have min and max
	// cout 6 row
	for ( i = 1; i <= Divs; i++ )
	{
		subdivide( ruler, iMin, iMax, i); // transfer i
		cout << ruler << endl;
		// resume array is NULL
		for ( int j = i; j < Len - 2; j++ )
		{
			ruler[ j ] = ' ';
		}
	}
	return 0;
}

在subdivide()函式,使用便利level來控制遞迴層。subdivide()函式呼叫自己兩次,一次針對左半部分,另一次針對右半部分。也就是說,呼叫一次導致兩個呼叫,然後導致4個呼叫,在導致8個呼叫,以此類推。這就是6層呼叫能夠填充64個元素的原因pow( 2, 6 )=64。這將不斷導致函式呼叫數(以及儲存的變數數)翻倍,因此如果要求的遞迴層次很多,這種遞迴方式將是一種糟糕的選擇;然而,如果遞迴層次較少,這將是一種精緻而簡單的選擇。

函式指標
歷史與邏輯
為何pf和(*pf)等家呢?一種學派認為,由於pf是函式指標,而*pf是函式,因此應將(*pf)()用作函式呼叫;另一種學派認為,由於函式名師指向該函式的指標,指向函式的指標的行為應與函式名相似,因此應將pf()用作函式呼叫使用。C++進行了折中----這兩種方式都是正確的,或者至少是允許的,雖然它們在邏輯上是相互衝突的。在認為折中折中粗糙之前,應該想遠類思維活動的特點。

函式指標示例:
1)使用typedef

#include "stdafx.h"
#include "iostream"
using namespace std;

double betsy( int );
double pam( int );

typedef double (*estimate)( int );

int _tmain(int argc, _TCHAR* argv[])
{
	int iValue = 5;
	estimate estimateFun;

	estimateFun = betsy;
	cout << "transfer betsy:"	<< estimateFun( iValue ) << endl;
	estimateFun = pam;
	cout << "transfer pam:"	<< estimateFun( iValue ) << endl;
	return 0;
}

double betsy( int iValue )
{
	return ( iValue * iValue );
}
double pam( int iValue )
{
	return ( iValue * 0.89 );
}


2)直接使用

#include "stdafx.h"
#include "iostream"
using namespace std;

double betsy( int );
double pam( int );

double estimateFun( int iValue, double ( *pf )(int) );

int _tmain(int argc, _TCHAR* argv[])
{
	int iValue = 5;
	cout << "transfer betsy:"	<< estimateFun( iValue, betsy) << endl;
	cout << "transfer pam:"	<< estimateFun( iValue,pam ) << endl;
	return 0;
}

double betsy( int iValue )
{
	return ( iValue * iValue );
}
double pam( int iValue )
{
	return ( iValue * 0.89 );
}
double estimateFun( int iValue, double ( *pf )(int) )
{
	return ( *pf )( iValue );
}


C++函式
1)行內函數
    常規函式:在執行到函式呼叫指令時,程式將在函式呼叫後立即儲存該指令的記憶體地址,並將函式引數複製到堆疊(為此保留的記憶體塊),跳到標記函式起點的記憶體單元,執行函式程式碼,然後跳回到地址被儲存的指令處。來回跳躍並記錄跳躍位置以為著使用函式時,需要一定的開銷。
    C++行內函數提供了另一種選擇。內聯汗水的編譯程式碼與其他程式程式碼"內聯"起來了。也就是說,編譯器將使用相應的函式程式碼替換函式呼叫。對於內聯程式碼,程式無須跳到另一個位置跳到另一個位置處執行程式碼,然後再跳回來。因此,行內函數的執行速度比常規函式稍快,但代價是需要佔用更多的記憶體。如果程式在10個不同的地方呼叫同一個行內函數,則該程式將包含該函式的10個程式碼拷貝。

#include "stdafx.h"
#include "iostream"
using namespace std;

inline int sum( int iLeftValue, int iRightValue )
{
	return ( iLeftValue + iRightValue );
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 10;
	int b = 14;
	cout << sum( a , b ) << endl;

	return 0;
}


2)引用作為函式的引數
引用作為某個變數的別名而存在
他與指標的區別在於,宣告的時候就必須進行初始化

#include "stdafx.h"
#include "iostream"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 14;
	int b = 10;
	int& ra = a;
	cout << "current a value is:" << a << endl;
	cout << "current reference ra value is:" << ra << endl;
	ra++;
	cout << "current a value is:" << a << endl;
	cout << "current reference ra value is:" << ra << endl;
	ra = b;
	cout << "current b value is:" << b << endl;
	cout << "current reference ra value is:" << ra << endl;

	return 0;
}


將引用作為函式的引數,以達到不進行按值傳遞的目的

#include "stdafx.h"
#include "iostream"
using namespace std;

void sum( int& a )
{
	a++;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 14;
	cout << "current a value is:" << a << endl;
	sum( a );
	cout << "current a value is:" << a << endl;

	return 0;
}

3)物件、繼承、引用
    簡單的說,ostream是基類,而ofstream是派生類。派生類繼承了基類的方法,這意味著ofstream物件可以使用基類的特性,如格式化方法precision()和self()
    繼承的另一個特徵是基類引用可以指向派生類物件,而無需進行強制型別轉換。這種特徵的一個實際結果是,可以定義一個接受基類引用作為引數的函式,呼叫該函式時,可以將基類物件作為引數,也可以將派生類物件作為引數。例如,引數型別為ostream&的函式可以接受ostream物件(如cout)或ofstream物件作為引數

4)何時使用引用引數
a.程式設計師能夠修改呼叫函式中的資料物件
b.通過傳遞引用而不是整個資料物件,可以提高程式的執行速度
5)何時使用按值傳遞
a.如果資料物件很小,如內建資料型別或小型結構,則按值傳遞
b.如果資料物件是陣列,則使用指標,因為這是唯一的選擇,並將指標宣告為指向const的指標
c.如果資料物件時較大的結構,則使用const指標或const引用,以提高程式的效率,這樣可以節省複製結構所需的時間和空間
d.如果資料物件是類物件,則使用const引用。類設計的語義常常要求使用引用,這是C++新增這項特性的主要原因。因此,傳遞類物件引數的標準方式是按引用傳遞


5)函式過載
引數個數不同構成的過載

#include "stdafx.h"
#include "iostream"
using namespace std;

void sum( int iLeftValue, int iRightValue )
{
}
void sum( int iLeftValue, int iMidValue, int iRightValue )
{
}


引數型別不同構成的過載

#include "stdafx.h"
#include "iostream"
using namespace std;

void sum( int iLeftValue, int iRightValue )
{
}
void sum( double fLeftValue, double fMidValue )
{
}

其他的比如,返回值型別,不能區別一個函式


6)函式模板
函式模板是通用的函式描述,也就是說它們使用通用型別來定義函式,其中的通用型別可用具體的型別(如int或double)替換。通過將型別作為引數傳遞給模板,可使編譯器生成該型別的函式。由於模板允許通用型別(而不是具體型別)的方式編寫程式,因此有時也被稱為通用程式設計。由於型別是用引數表示的,因此模板特性有時也被稱為引數化型別(parametarized types)。

#include "stdafx.h"
#include "iostream"
using namespace std;

/*
template<class T>
void Swap( T* a, T* b )
{
	T temp;
	temp = *a;
	*a = *b;
	*b = temp;
}*/
template<class T>
void Swap( T& a, T& b )
{
	T temp;
	temp = a;
	a = b;
	b = temp;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int ia = 14;
	int ib = 10;
	cout << "current ia value is:" << ia << endl;
	cout << "current b value is:" << ib << endl;
	Swap<int>( ia, ib );
	cout << "current ia value is:" << ia << endl;
	cout << "current ib value is:" << ib << endl;

	cout << "\n";
	double fa = 14.4;
	double fb = 10.4;
	cout << "current fa value is:" << fa << endl;
	cout << "current fb value is:" << fb << endl;
	Swap<double>( fa, fb );
	cout << "current fa value is:" << fa << endl;
	cout << "current fb value is:" << fb << endl;

	return 0;
}


a、過載的函式模板
需要多個對不同型別使用同一種演算法的函式時,可使用模板。不過,並非所有的型別都使用相同的演算法。為滿足這種需求,可以像過載常規函式定義那樣過載函式模板定義。和常規過載一樣,被過載的模板的函式特徵為( T&, T& )。而新模板的特徵為( T[], T[], int ),最後一個引數的型別為具體型別( int ),而不是通用型別。並非所有的模板引數都必須是模板引數型別。

#include "stdafx.h"
#include "iostream"
using namespace std;

#define ARRAYMAXLENGTH	4

template<class T>
void Swap( T& a, T& b )
{
	T temp;
	temp = a;
	a = b;
	b = temp;
}

template<class T>
void Swap( T arr[], T brr[], const int nLength )
{
	T temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr[ i ] = temp;
	}
}

template<class T>
void DisplayArray( const T arr[], int nLength )
{
	for ( int i = 0; i < nLength; i++ )
	{
		cout << "current " << i << " value is:" << arr[ i ] << endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int ia = 14;
	int ib = 10;
	cout << "current ia value is:" << ia << endl;
	cout << "current b value is:" << ib << endl;
	Swap<int>( ia, ib );
	cout << "current ia value is:" << ia << endl;
	cout << "current ib value is:" << ib << endl;

	cout << "\n";
	int arr[] = { 1, 2, 3, 4 };
	int brr[] = { 9, 8, 7, 6 };
	DisplayArray<int>( arr, ARRAYMAXLENGTH );
	Swap<int>( arr, brr, ARRAYMAXLENGTH );
	cout << "\n";
	DisplayArray<int>( arr, ARRAYMAXLENGTH );

	return 0;
}

b、模板的顯示具體化
對於給定的函式名,可以有非模板函式、模板函式和顯示具體化模板函式以及他們的過載版本。
顯示具體化的圓形和定義應以template<>打頭,並通過名稱來指出型別。
具體化將覆蓋常規模板,而非模板函式將覆蓋具體化和常規模板。

#include "stdafx.h"
#include "iostream"
using namespace std;

#define ARRAYMAXLENGTH	4

template<class T>
void Swap( T arr[], T brr[], const int nLength )
{
	T temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr[ i ] = temp;
	}
}
template<>
void Swap( double arr[], double brr[], const int nLength)
{
	double temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr [ i ] = temp;
	}
	cout << "enter in this function!" << endl;
}
template<class T>
void DisplayArray( const T arr[], int nLength )
{
	for ( int i = 0; i < nLength; i++ )
	{
		cout << "current " << i << " value is:" << arr[ i ] << endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << "\n";
	int arr[] = { 1, 2, 3, 4 };
	int brr[] = { 9, 8, 7, 6 };
	DisplayArray<int>( arr, ARRAYMAXLENGTH );
	Swap<int>( arr, brr, ARRAYMAXLENGTH );
	cout << "\n";
	DisplayArray<int>( arr, ARRAYMAXLENGTH );

	cout << "\n";
	double dArr[] = { 1.1, 2.2, 3.3, 4.4 };
	double dBrr[] = { 9.9, 8.8, 7.7, 6.6 };
	DisplayArray<double>( dArr, ARRAYMAXLENGTH );
	Swap<double>( dArr, dBrr, ARRAYMAXLENGTH );
	cout << "\n";
	DisplayArray<double>( dArr, ARRAYMAXLENGTH );

	return 0;
}


c、非模板函式和模板函式共存
如果不是對模板進行例項化,比如:
Swap<double>( dArr, dBrr, ARRAYMAXLENGTH );
的呼叫,那麼呼叫模板函式,如果呼叫形式是Swap( dArr, dBrr, ARRAYMAXLENGTH );,則優先呼叫非模板函式

#include "stdafx.h"
#include "iostream"
using namespace std;

#define ARRAYMAXLENGTH	4

template<class T>
void Swap( T arr[], T brr[], const int nLength )
{
	T temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr[ i ] = temp;
	}
}

template<>
void Swap( double arr[], double brr[], const int nLength)
{
	double temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr [ i ] = temp;
	}
	cout << "enter in this function1!" << endl;
}

void Swap( double arr[], double brr[], const int nLength)
{
	double temp;
	for ( int i = 0; i < nLength; i++ )
	{
		temp = arr[ i ];
		arr[ i ] = brr[ i ];
		brr [ i ] = temp;
	}
	cout << "enter in this function2!" << endl;
}

template<class T>
void DisplayArray( const T arr[], int nLength )
{
	for ( int i = 0; i < nLength; i++ )
	{
		cout << "current " << i << " value is:" << arr[ i ] << endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << "\n";
	int arr[] = { 1, 2, 3, 4 };
	int brr[] = { 9, 8, 7, 6 };
	DisplayArray<int>( arr, ARRAYMAXLENGTH );
	Swap<int>( arr, brr, ARRAYMAXLENGTH );
	cout << "\n";
	DisplayArray<int>( arr, ARRAYMAXLENGTH );

	cout << "\n";
	double dArr[] = { 1.1, 2.2, 3.3, 4.4 };
	double dBrr[] = { 9.9, 8.8, 7.7, 6.6 };
	DisplayArray<double>( dArr, ARRAYMAXLENGTH );
	Swap( dArr, dBrr, ARRAYMAXLENGTH );
	cout << "\n";
	DisplayArray<double>( dArr, ARRAYMAXLENGTH );

	return 0;
}


限定符volatile、mutable
    volatile關鍵字表明,即使程式程式碼沒有對記憶體單元進行修改,其值也可能發生變化。例如,可以將一個指標指向某個硬體的位置,其實包含了來自串列埠的時間或資訊。在這種情況下,硬體(而不是程式)了能修改其中的內容。或者兩個程式可能互相影響,共享資料。該關鍵字的作用是為了改善編譯器的優化能力。例如,假設編譯器發現,程式在幾條語句中兩次使用了某個變數的值,則編譯器可能不是讓程式查詢這個值兩次,而是將這個值快取到暫存器中。這種優化假設變數的值在這兩次使用之間不會發生變化。如果不將變數宣告為volatile,則編譯器將進行這種優化;將變數宣告為volatile,相當於告訴編譯器,不要進行這種優化。
    mutable,可以用它來指出,即使結構(或類)變數為const,其某個成員也可以被修改。

#include "stdafx.h"
#include "iostream"
using namespace std;

struct Student 
{
	mutable int iAge;
	mutable char szName[ 10 ];
};

int _tmain(int argc, _TCHAR* argv[])
{
	const Student stu = { 23, "zengraoli" };
	cout << "current student age is:" << stu.iAge << endl;
	cout << "current student name is:" << stu.szName << endl;

	stu.iAge = 24;
	memcpy( stu.szName, "zeng", sizeof("zeng") );
	cout << "\n";
	cout << "current student age is:" << stu.iAge << endl;
	cout << "current student name is:" << stu.szName << endl;


	return 0;
}


顯示指出呼叫約定
在C語言中,一個名稱只對應一個函式,因此這很容易實現。因此,為滿足內部需要,C語言編譯器可能將max這樣的函式名翻譯成_max。這種方法被稱為C語言連結性(C language linkage)。但在C++中,同一個名稱可能對應多個函式,必須將這些函式翻譯為不同的符號名稱。因此,C++編譯器執行名稱糾正或名稱修飾,為過載函式生成不同的函式名稱。例如,可能將max2( int,int )轉換成_max2_i_i,而將max2( double, double )轉換成_max_d_d。這種方法稱為C++語言連結。
當然,可以顯示的指出呼叫約定:

extern "C" int max( int a, int b )
{
	return ( (a > b ) ? a : b );
}

extern "C++" int max2( int a, int b )
{
	return ( (a > b ) ? a : b );
}


名稱空間
1)未命名的名稱空間
可以通過省略名稱空間的名稱來建立未命名的名稱空間
namespace
{
 int iValue;
 int IAge;
}
這就像後面跟著using編譯指令一樣,也就是說,在該名稱空間中宣告的名稱潛在作用於為:從宣告點到該宣告區域末尾。從這個方面看,他們與全域性變數相似。不過,由於這種名稱空間沒有名稱,因此不能顯示地使用using編譯指令或using宣告來使它在其他位置都可用。具體地說,不能在未命名名稱空間所屬檔案之外的其他檔案中,使用該名稱空間中的名稱,因此這種方法可以替代連結性為內部的靜態變數。

#include "stdafx.h"

namespace
{
	int iValue;
	int IAge;
}

int _tmain(int argc, _TCHAR* argv[])
{
	iValue = 14;
	IAge = 39;

	return 0;
}

2)名稱空間及其前途
隨著程式設計師逐漸熟悉名稱空間,將出現同一的程式設計理念。下面是當前的一些指導原則:
a、使用在已命名的名稱空間中宣告的變數,而不是使用外部全域性變數
b、使用在已命名的名稱空間中宣告的變數,而不是使用靜態全域性變數
c、如果開發了一個函式庫或類庫,講起放在一個名稱空間中。
d、僅將編譯指令using作為一種將舊程式碼轉換為使用名稱空間的權益之計
e、不要再標頭檔案中使用using編譯指令。首先,這樣做掩蓋了要讓哪些名稱可用;另外,包含標頭檔案的順序可能影響程式的行為。如果非要使用編譯指令using,應將其放在所有前處理器編譯指令#include之後
f、匯入名稱時,首選使用作用域解析操作符或using宣告的方法
g、對於using宣告,首選將其作用域設定為區域性而不是全域性。
使用名稱空間的主旨是簡化大型程式設計專案的管理工作。對於只有一個檔案的簡單程式,使用using編譯指令並非什麼大逆不道的事。


抽象和類
    生活中充滿複雜性,處理複雜性的方法之一是簡化和抽象。人的身體是由無數個原子組成的,而一些學者認為人的思想是由半自主的主體組成的。但將人自己看做一個實體將簡單得多。在計算中,為了根據資訊與使用者之間的介面來表示他,抽象是至關重要的。也就是說,將問題的本質特徵抽象出來,並根據特徵來描抽象是通往使用者定義型別的捷徑,在C++中,使用者定義型別指的是實現抽象介面的類的設計。

介面
    介面是一個共享框架,供兩個系統互動時使用;例如,使用者可能是自己,而程式可能是字處理器。使用字處理器時,不能直接將腦子中想到的詞傳輸到計算機記憶體中,而必須同程式提供的介面互動、敲打鍵盤時,計算機將字元顯示到螢幕上;移動滑鼠時,計算機移動螢幕上的游標。
    對於類,所說的公共介面。在這裡,公眾(public)是使用類的程式,互動系統由類物件組成。而介面由編寫類的人提供的方法組成,介面讓程式設計師能夠編寫與類物件互動的程式碼。從而讓程式能夠使用類物件。例如,要計算string物件中包含多少個字元,無須開啟物件,而只需使用string類提供的size()方法。類設計禁止公共使用者直接訪問類。但公眾可以使用size()方法。size()方法是使用者和string類物件之間的公共介面的組成部分。通常,方法getline()是istream類的公共介面的組成部分,使用cin的程式不是直接與cin物件內部互動來讀取一行輸入,而是使用getline();

this指標
    每個成員函式(包括建構函式和解構函式)都有一個this指標。this指標指向呼叫物件。如果方法需要引用整個呼叫物件,則可以使用表示式*this。在函式的括號後面使用const限定符將this限定為const,這樣將不能使用this來修改物件的值。

仔細選擇資料型別,使類最小
    在設計類時,應認真考慮類成員的資料型別。貿然使用非標準或依賴於平臺的資料型別將使類急劇增大,從而增加所需的記憶體或程式的工作了。這種做法既低效又非常糟糕。
    與boo(在多數平臺上通常只佔1個位元組)不同的是,每個BOOL通常都將佔據4個位元組。如果類成員的用途是管理Boolean值(true或false),則實際只需要1個位元組。

實現一個堆疊的類
1)可建立空堆疊
2)可將資料項新增到棧頂(壓入)
3)可從棧頂刪除資料項(彈出)
4)可檢視堆疊是否填滿
5)可檢視堆疊是否為空
可以將上述描述轉換為一個類宣告,其中公有成員函式提供了表示堆疊操作的介面,而私有資料成員負責儲存堆疊資料。

// testStack.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include "iostream"
using namespace std;

typedef int MyType;

template< typename Item >
class Stack
{
public:
	Stack()
	{
		top = 0;
		memset( data, 0, sizeof( data ) );
	}
	bool Push( const Item item )
	{
		if ( MAX == top )
		{
			return false;
		}
		data[ top++ ] = item;
		return true;
	}
	bool Pop( Item &item )
	{
		if ( 0 == top )
		{
			return false;
		}
		item = data[ --top ];
		return true;
	}
	bool IsEmpty()
	{
		return ( 0 == top );
	}
	bool IsFull()
	{
		return ( MAX == top );
	}
	void Print()
	{
		for ( int i = 0; i < top; i++ )
		{
			cout << "the " << i << " value is:" << data[ i ] << endl;
		}
	}
private:
	enum { MAX = 10 };
	Item data[ MAX ];
	int top;
};

int _tmain(int argc, _TCHAR* argv[])
{
	int i;
	MyType temp;
	Stack<MyType> test;
	cout << "isEmpty:" << test.IsEmpty() << endl;

	for ( i = 0; i <= 9; i++ )
	{
		test.Push( i + 1 );
	}
	cout << "isFull:" << test.IsFull() << endl;

	if ( !test.Push( 11 ) )
	{
		cout << "push failure!" << endl;
	}
	
	for ( i = 0; i <= 9; i++ )
	{
		if ( test.Pop( temp ))
		{
			cout << "pop a elem:" << temp << endl;
		}
	}
	
	if ( !test.Push( 11 ) )
	{
		cout << "push failure!" << endl;
	}
	test.Print();

	return 0;
}


使用類
1)類中一個加法操作符的過載例子

#include "stdafx.h"
#include "string"
#include "iostream"
using namespace std;

namespace
{
	class CTest_A
	{
	public:
		CTest_A( int nValue, string strName )
		{
			m_nAge = nValue;
			m_strName = strName;
		}
		// override operator +
		CTest_A operator +( const CTest_A& rCTest_A ) const
		{
			m_nAge += rCTest_A.m_nAge;
			return *this;
		}
		~CTest_A(){}
		inline int GetAge() const
		{
			return m_nAge;
		}
		inline string GetName() const
		{
			return m_strName;
		}
	private:
		int m_nAge;
		string m_strName;
	};
}

int _tmain(int argc, _TCHAR* argv[])
{
	CTest_A CA( 23, "zengraoli" );
	CTest_A CB( 23, "zengraoli2" );
	CB = CB +CA;

	cout << "current student name is:" << CB.GetName() << endl;
	cout << "current student age is:" << CB.GetAge() << endl;

	return 0;
}

2)過載限制
多數C++操作符都可以用這樣的方式過載。過載的操作符(有些例外情況)不必是成員函式,但必須至少有一個運算元是使用者定義的型別。
a、過載後的操作符必須至少有一個運算元使用使用者自定的型別,這將防止使用者為標準型別過載操作符。因此,不恩能夠將減法操作符(-)過載為計算兩個double值的和,而不是他們的差。雖然這種限制將對創造性有所影響,但可以確保程式正常執行。
b、使用操作符時不能違反操作符原來的句法規則。例如,不能將求模操作符(%)過載成使用一個運算元。通用不能修改操作符的優先順序。因此,如果將加好操作符過載成將兩個類相加,則新的操作符與原來的加好具有相同的優
先級。
c、不能定義新的操作符。例如,不能定義operator**()函式裡表示求冪。

3)為何需要友元
在位類過載二院操作符時(帶兩個引數的操作符)常常需要用到友元。將Time物件乘以實數就屬於這種情況。乘法的操作符使用了兩種不同的型別,也就是說,假髮惡化減法操作符都結合連個Time值,而乘法操作符將一個Time值與一個double值結合在一起。這限制了該操作符的使用方式。左側的運算元是呼叫物件。也就是說,下面的語句:
A = B * 2.75;將被轉換為下面的成員函式呼叫:A = B.operator*( 2.75 );但是如果寫成A = 2.75 * B;因為2.75不是Time型別的物件。因此,編譯器不能使用成員函式呼叫來替換該表示式。

所以這個時候,要把乘法過載為非成員函式(大多是操作符都可以通過成員或非成員函式來過載)。非成員函式不是由物件呼叫的,他使用的所有值(包括物件)都是顯示引數。這樣,編譯器就能夠順利編譯A = 2.75 * B;

#include "stdafx.h"
#include "iostream"
using namespace std;

namespace
{
	class CTime
	{
	public:
		CTime( int nHours = 0, int nMiniutes= 0 ) 
			:m_nHours( nHours ), m_nMiniutes( nMiniutes )
		{
		}
	friend CTime operator*( double ftime, const CTime& ct );
	inline int GetHours() const
	{
		return m_nHours;
	}
	inline int GetMiniute() const
	{
		return m_nMiniutes;
	}
	private:
		int m_nHours;
		int m_nMiniutes;
	};
	CTime operator*( double ftime, const CTime& ct )
	{
		CTime result;
		long totalminutes = static_cast<long>( ct.m_nHours * ftime * 60 + ct.m_nMiniutes * ftime );
		result.m_nHours = totalminutes / 60;
		result.m_nMiniutes = totalminutes % 60;
		return result;
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	CTime CA( 4, 10 );
	CA = 2.0 * CA;
	cout << "current hours is:" << CA.GetHours() << "  " << "current miniutes is:" << CA.GetMiniute() << endl;

	return 0;
}


4)常用的友元:過載<<操作符
    一個很有用的類特性是,可以對<<操作符進行過載,使之能與cout一起來顯示物件的內容。當輸出time物件的時候,可以直接使用cout << time;之所以可以這樣做,是因為<<是可被過載的C++操作符之一。實際上,它已經被過載很多次了。最初,他表示額含義是按位移。ostream類對該操作符進行了過載,將其轉換為一個輸出工具。
    在過載<<的時候應使用cout物件本身(void operator<<( ostream& os, CTime& ct )),而不是他的拷貝。因此該函式按應用(而不是按值)來傳遞該物件。這樣,表示式cout << time;將導致os成為cout的一個別名;而表示式cout << time;將導致os成為cerr的另一個別名。

#include "stdafx.h"
#include "iostream"
using namespace std;

namespace
{
	class CTime
	{
	public:
		CTime( int nHours = 0, int nMiniutes= 0 ) 
			:m_nHours( nHours ), m_nMiniutes( nMiniutes )
		{
		}
	friend CTime operator*( double ftime, const CTime& ct );
	inline int GetHours() const
	{
		return m_nHours;
	}
	inline int GetMiniute() const
	{
		return m_nMiniutes;
	}
	private:
		int m_nHours;
		int m_nMiniutes;
	};
	CTime operator*( double ftime, const CTime& ct )
	{
		CTime result;
		long totalminutes = static_cast<long>( ct.m_nHours * ftime * 60 + ct.m_nMiniutes * ftime );
		result.m_nHours = totalminutes / 60;
		result.m_nMiniutes = totalminutes % 60;
		return result;
	}
	ostream& operator<<( ostream& os, CTime& ct )
	{
		os << "current hours is:" << ct.GetHours() << "  " << "current miniutes is:" << ct.GetMiniute() << endl;
		return os;
	}
}


int _tmain(int argc, _TCHAR* argv[])
{
	CTime CA( 4, 10 );
	CA = 2.0 * CA;
//	cout << "current hours is:" << CA.GetHours() << "  " << "current miniutes is:" << CA.GetMiniute() << endl;
	cout << CA << CA;
	return 0;
}

類的自動轉換和強制型別轉換
1)如果建構函式中含有類似的拷貝函式:

CStonewt( double lbs )
{
 m_nStone = int( lbs ) / Lbs_per_stn;
 m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs );
 m_fPounds = lbs;
}

那麼使用
CStonewt myCat;
myCat = 19;
程式將使用建構函式CStonewt( double lbs )來建立一個臨時的CStonewt物件,並將19.6作為初始值。隨後,採用逐成員賦值方式將該臨時物件的內容賦值到myCat中(比如m_nStone = int(

lbs ) / Lbs_per_stn;)。這一過程稱為隱式轉換,因為他是自動進行的,而不需要顯示強制型別轉換。如果換成CStonewt( double lbs, int i )有兩個引數,因此不能用來轉換型別。

#include "stdafx.h"
#include "iostream"
using namespace std;

namespace
{
	class CStonewt
	{
	public:
		CStonewt( double lbs )
		{
			m_nStone = int( lbs ) / Lbs_per_stn;
			m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs );
			m_fPounds = lbs;
		}
		CStonewt( int stn, double lbs )
		{
			m_nStone = stn;
			m_fPds_left = lbs;
			m_fPounds = stn * Lbs_per_stn + lbs;
		}
		CStonewt()
		{
			m_nStone = m_fPounds = m_fPds_left = 0;
		}
		~CStonewt()
		{

		}
		void show_lbs() const
		{
			cout << m_nStone << " stone" << m_fPds_left << " pound\n" << endl;;
		}
		void show_stn() const
		{
			cout << m_fPounds << " pound\n" << endl;	
		}
	private:
		enum { Lbs_per_stn = 14 };
		int m_nStone;
		double m_fPds_left;
		double m_fPounds;
	};
}

int _tmain(int argc, _TCHAR* argv[])
{
	CStonewt myCat;
	myCat = 19;

	return 0;
}

2)將建構函式用作自動型別轉換函式似乎是一項不錯的特性。不過,當程式設計師擁有更豐富的C++經驗時,將發現這種自動也行並非總是合乎需要的,因為這會導致意外的型別轉換。因此,最新

的C++實現新增了一個關鍵字(explicit),用來關閉這種自動特性。也就是說,可以這樣宣告建構函式

explicit CStonewt( double lbs )
{
	m_nStone = int( lbs ) / Lbs_per_stn;
	m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs );
	m_fPounds = lbs;
}

但此時仍然可以進行myCat = (CStonewt)19.5;強制型別轉換。

3)把CStonewt類物件賦值給int、double變數
要進行這樣的操作時,編譯器發現右側是CStonewt型別,而左側是int、double型別,因此它將檢視程式設計師是否定義了與此匹配的轉換函式(如果沒有找到這樣的定義,編譯器將生成錯誤訊息

,指出無法將CStonewt賦給int、double)
如果想要使用這種轉換函式,要轉換為typeName型別,需要使用這種形式的轉換函式:
operator typeName();
注意以下幾點:
a、轉換函式必須是類方法
b、轉換函式不能指定返回型別
c、轉換函式不能有引數

#include "stdafx.h"
#include "iostream"
using namespace std;

namespace
{
	class CStonewt
	{
	public:
		explicit CStonewt( double lbs )
		{
			m_nStone = int( lbs ) / Lbs_per_stn;
			m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs );
			m_fPounds = lbs;
		}
		CStonewt( int stn, double lbs )
		{
			m_nStone = stn;
			m_fPds_left = lbs;
			m_fPounds = stn * Lbs_per_stn + lbs;
		}
		CStonewt()
		{
			m_nStone = m_fPounds = m_fPds_left = 0;
		}
		~CStonewt(){}
		operator int() const
		{
			return int( 100.5 );
		}
		operator double() const
		{
			return  999.5 ;
		}
	private:
		enum { Lbs_per_stn = 14 };
		int m_nStone;
		double m_fPds_left;
		double m_fPounds;
	};
}

int _tmain(int argc, _TCHAR* argv[])
{
	CStonewt myCat;
	myCat = (CStonewt)19.5;
	double fValue = myCat;
	int iValue = myCat;
	cout << "iValue is:" << iValue  << endl;
	cout << "fValue is:" << fValue  << endl;

	return 0;
}


類和動態記憶體分配
    小插曲 : strlen()返回字串長度,但不包括末尾的空字元,因此建構函式len + 1,使分配的記憶體能夠儲存包含空字元的字串
1)含有很多隱藏錯誤的stringBad類

StringBad.h:
#ifndef _STRINGBAD_H_
#define _STRINGBAD_H_

#include "iostream"
#include "string"
using namespace std;

class StringBad
{
public:
	StringBad();
	StringBad( const char* str );
	~StringBad();

	friend ostream& operator<< ( ostream& os, const StringBad& sb );
private:
	char* m_str;
	int m_nLen;
public:
	static int num_strings;
};

#endif


StringBad.cpp:

#include "stdafx.h"
#include "StringBad.h"

int StringBad::num_strings = 0;

StringBad::StringBad()
{
	m_nLen = 4;
	m_str = new char[ 4 ];
	num_strings++;
	strcpy_s( m_str, strlen( "C++" ) + 1,"C++" );
	cout << StringBad::num_strings << ": \"" << m_str << "\" object created" << endl;
}

StringBad::StringBad( const char* str )
{
	m_nLen = strlen( str );
	m_str = new char[ m_nLen + 1 ];
	num_strings++;
	strcpy_s( m_str,m_nLen + 1 , str );
	cout << num_strings << ": \"" << m_str << "\" object created" << endl;
}

StringBad::~StringBad()
{
	if ( m_str )
	{
		delete[] m_str;
	}
	num_strings--;
	cout << "in the StringBad::~StringBad() num_strings is:" << num_strings << endl;
}

ostream& operator<< ( ostream& os, const StringBad& sb )
{
	os << "this StringBad str is:" << sb.m_str << endl;
	os << "this StringBad len is:" << sb.m_nLen << endl;
	return os;
}


testStringBad.cpp:

[cpp] view plaincopyprint?
  1. #include "stdafx.h"
  2. #include "iostream"
  3. usingnamespace std;  
  4. #include "StringBad.h"
  5. void callme1( StringBad& rStringBad )  
  6. {  
  7.     cout << "in the function callme1" << endl;  
  8.     cout << "in the function callme1" << rStringBad << endl;  
  9. }  
  10. void callme2( StringBad stringBad )  
  11. {  
  12.     cout << "in the function callme2" << endl;  
  13.     cout << "in the function callme1" << stringBad << endl;  
  14. }  
  15. int _tmain(int argc, _TCHAR* argv[])  
  16. {  
  17.     StringBad headline1( "Create headline1" );  
  18.     StringBad headline2( "Create headline2" );  
  19.     StringBad sport( "Create sport" );  
  20.     cout << headline1 << endl;  
  21.     cout << headline2 << endl;  
  22.     cout << sport << endl;  
  23.     callme1( headline1 );  
  24.     cout << headline1 << endl;  
  25.     callme2( headline2 );  
  26.     cout << headline2 << endl;  
  27.     cout << "Initialize one object to another:" << endl;  
  28.     StringBad sailer = sport;  
  29.     cout << "sailer" << sailer << endl;  
  30.     cout << "Assign one object to anther:" << endl;  
  31.     StringBad knot;  
  32.     knot = headline1;  
  33.     cout << "knot" << knot << endl;  
  34.     cout << "End of main()" << endl;  
  35.     cout << "num_strings is:" << StringBad::num_strings << endl;  
  36.     return 0;  
  37. }  
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "StringBad.h"

void callme1( StringBad& rStringBad )
{
	cout << "in the function callme1" << endl;
	cout << "in the function callme1" << rStringBad << endl;
}

void callme2( StringBad stringBad )
{
	cout << "in the function callme2" << endl;
	cout << "in the function callme1" << stringBad << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	StringBad headline1( "Create headline1" );
	StringBad headline2( "Create headline2" );
	StringBad sport( "Create sport" );

	cout << headline1 << endl;
	cout << headline2 << endl;
	cout << sport << endl;

	callme1( headline1 );
	cout << headline1 << endl;
	callme2( headline2 );
	cout << headline2 << endl;

	cout << "Initialize one object to another:" << endl;
	StringBad sailer = sport;
	cout << "sailer" << sailer << endl;
	cout << "Assign one object to anther:" << endl;

	StringBad knot;
	knot = headline1;
	cout << "knot" << knot << endl;
	cout << "End of main()" << endl;

	cout << "num_strings is:" << StringBad::num_strings << endl;

	return 0;
}


2)
複製拷貝函式

[cpp] view plaincopyprint?
  1. StringBad& StringBad::operator= ( const StringBad& st)  
  2. {  
  3.     ifthis == &st )  
  4.     {  
  5.         return *this;  
  6.     }  
  7.     delete[] str;  
  8.     len = st.len;  
  9.     str = newchar[ strlen( len + 1 ) ];  
  10.     str::strcpy( str, st.str );  
  11.     return *this;  
  12. }  
StringBad& StringBad::operator= ( const StringBad& st)
{
	if( this == &st )
	{
		return *this;
	}
	delete[] str;
	len = st.len;
	str = new char[ strlen( len + 1 ) ];
	str::strcpy( str, st.str );
	return *this;
}


3)
重寫下標運算子

[cpp] view plaincopyprint?
  1. char& operator[] ( int i )  
  2. {  
  3.  return m_str[ i ];  
  4. }  
  5. constchar& operator[] ( int i ) const
  6. {  
  7.  return m_str[ i ];  
  8. }  
char& operator[] ( int i )
{
 return m_str[ i ];
}
const char& operator[] ( int i ) const
{
 return m_str[ i ];
}

為什麼要提供兩個版本。原因是m_str是常量,而上述方法無法確保不修改資料。
但在過載時,C++將區分常量和非常量函式的特徵標,因此提供了另一個僅供const String物件使用的
operator[]()版本

4)
新的String類

相關推薦

C++Primer PLus 讀書筆記

處理第一個問題: 1)某書店以檔案形式儲存其每一筆交易。沒一筆交易記錄某本書的銷售情況,含有ISBM、銷售冊數和銷售單 價。每一筆交易形如:0-201-70352-X 4 24.99 -----------------------------------------

C Primer Plus (6) 讀書筆記_Chapter 1

抽象 ner 競爭 crete 個數字 面向 ref 編程 bsd 第 1 章 初識 C 語言 ■ C 的歷史和特性 ■ 編寫程序的步驟 ■ 編譯器和鏈接器的一些知識 ■ C 標準 1.1 C 語言的起源 1972年,貝爾實驗室的 丹尼斯 ? 裏奇

C Primer Plus ()中文版—— 4 章 字串和格式化輸入輸出

4.1  前導程式 #include <stdio.h> #include <string.h> /*提供strlen() 函式原型*/ #define PRAISE "You are my sunshine!!!" int main(void

C++ Primer Plus 學習筆記三章

  1、檢視系統中各資料型別所佔的位元組數(sizeof),所能表示的最大和最小取值,標頭檔案climite中包含了關於整型限制的資訊,定義了所使用的各種符號常量 例:檢視各種整型資料型別所佔的位元組數以及所能表示的最大數值 #include "stdafx.h" #inclu

C Primer Plus ()中文版—— 13 章 檔案輸入/輸出

13.1  和檔案進行通訊 13.1.1  檔案是什麼 一個檔案(file)通常就是磁碟上的一段命名的儲存區。C 將檔案看成是連續的位元組序列,其中沒一個位元組單獨地讀取。ANSI C 提供了檔案的兩種檢視:文字檢視、二進位制檢視。 13.1.2  文字檢視

C Primer Plus ()中文版—— 12 章 儲存類、連結和記憶體管理

12.1  儲存類 12.1.1  作用域 定義:作用域描述了程式中可以訪問一個識別符號的一個或多個區域。 分類: 程式碼塊作用域:在程式碼塊中定義的變數具有程式碼塊作用域,從定義處到包含該定義的程式碼塊的末尾,該變數可見。 函式原型作用域:在函式原型

C Primer Plus ()中文版—— 11 章 字串和字串函式

11.1  字串表示和字串 I/O 11.1  在程式中定義字串 一、字串常量 字串常量(string constant)又稱字串文字(string literal),是指位於一對雙引號中的任何字元。字串常量屬於靜態儲存類。 可以用 #define 來定義字串常量

C Primer Plus ()中文版—— 10 章 陣列和指標

10.1  陣列 陣列(array)由一系列型別相同的元素構成。陣列宣告(array declaration)中包括陣列元素的數目和元素的型別。如: int month[12]; /* 12個整數的陣列 */ /* int 是陣列中

C Primer Plus ()中文版—— 9 章 函式

9.1  函式描述 函式(function)是用於完成特定任務的程式程式碼的自包含單元。一個簡單函式: /* lesser.c -- finds the lesser of two evils */ #include <stdio.h> int imin(in

C Primer Plus ()中文版—— 8 章 字元輸入/輸出和輸入確認

8.1  單字元 I/O:getchar() 和 putchar() getchar() 和 putchar() 每次輸入和輸出一個字元。一個輸入回顯例子: /*使用一個while迴圈,該迴圈在遇到#時終止*/ int main(void) { char ch; while

C primer plus()程式設計練習六章

第一題:編寫一個程式。建立一個具有26個元素的陣列;並在其中儲存26個小寫字母。並讓程式現實該陣列的內容。 解: 程式碼如下: #include <stdio.h> int main(void) {     char i,letters[26];     int

C Primer Plus ()中文版—— 9 章 函式

9.1  函式描述 函式(function)是用於完成特定任務的程式程式碼的自包含單元。一個簡單函式: /* lesser.c -- finds the lesser of two evils */ #include <stdio.h> int imin(int

C Primer Plus ()中文版—— 8 章 字元輸入/輸出和輸入確認

8.1  單字元 I/O:getchar() 和 putchar() getchar() 和 putchar() 每次輸入和輸出一個字元。一個輸入回顯例子: /*使用一個while迴圈,該迴圈在遇到#時終止*/ int main(void) { char ch; w

C Primer Plus () 章 程式設計練習 答案

1. 編寫一個程式。將用分鐘表示的時間轉換成以小時和分鐘表示的時間。使用#define或者const來建立一個代表60的符號常量。使用while迴圈來允許使用者重複鍵入值,並且當鍵入一個小於等於0的時間時終止迴圈。#include <stdio.h> #defi

C Primer Plus ()中文版—— 7 章 C 控制語句:分支和跳轉

7.1 if 語句 if 語句被稱為分支語句(branching statement)或選擇語句(selection statement),它提供了一個交匯點,在此處程式需要選擇兩條分支的一條前進。其一般形式為: if(expression) statement

C Primer Plus 筆記

1.  使用const 代替 #define 定義常量 原因:(1)const 宣告顯示指明瞭型別;(2)const可以很方便的用於複合型別,比如是陣列等;(3)作用域規則,const可以建立為全域性,名稱空間以及資料塊的常量。 2. 使用inline而不是#define

C primer plus()程式設計練習十二章

第一題:不使用全域性變數,重寫程式清單12.4中的程式。 解: 程式碼如下: #include <stdio.h> int critic(void); int main(void) {     int units = 0;     printf("How ma

C Primer Plus ()中文版—— 4 章 字串和格式化輸入輸出

4.1  前導程式 #include <stdio.h> #include <string.h> /*提供strlen() 函式原型*/ #define PRAISE "You are my sunshine!!!" int mai

Primer C++ 讀書筆記(一)

Primer C++第五版 讀書筆記(一) (如有侵權請通知本人,將第一時間刪文) 1.1-2.2 章節 關於C++變數初始化: 初始化不是賦值,初始化的含義是建立變數時賦予其一個初始值,而賦值的含義是把物件的當前值擦除,以一個新值來替代. 定義一個名為a的int變數並初始化為0,有以下4種方法

C++ Primer 讀書筆記 第一章 開始

下面是C++ Primer第一章的讀書筆記 ———————————————————————— 0001.作業系統如何執行C++程式 作業系統通過呼叫main來執行C++程式 0002.main 作業系統執行一個C++程式時所呼叫的函式 每個程式必須有且只有一個命名為mai