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類
#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:
- #include "stdafx.h"
- #include "iostream"
- usingnamespace 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;
- }
#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)
複製拷貝函式
- StringBad& StringBad::operator= ( const StringBad& st)
- {
- if( this == &st )
- {
- return *this;
- }
- delete[] str;
- len = st.len;
- str = newchar[ strlen( len + 1 ) ];
- str::strcpy( str, st.str );
- return *this;
- }
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)
重寫下標運算子
- char& operator[] ( int i )
- {
- return m_str[ i ];
- }
- constchar& operator[] ( int i ) const
- {
- return m_str[ i ];
- }
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