<<C++Primer PLus 第五版>>读书笔记1

来源:互联网 发布:c 二维数组赋值不全 编辑:程序博客网 时间:2024/04/28 00:11

处理第一个问题:
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修改其所指对象的值。
不能给一个常量赋值,常量指针所指的对象都不能修改:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. int _tmain(int argc, _TCHAR* argv[])  
  6. {  
  7.     double dbValue = 4;  
  8.     const double* cptr = &dbValue;  
  9.     *cptr = 5;  
  10.     return 0;  
  11. }</span>  


2)const指针
int iValue = 1;
int *const pNumber = &iValue;
此时,可以从右向左把这个定义语句读作"pNumber"是指向int型对象的const指针。与其他const变量一样,const指针的值也不能修改,这就意味着不能使pNumber指向其他对象。任何企图给const指针赋值的行为(即使pNumber赋回同样的值)都会导致编译错误。
pNumber = pNumber;
与任何const量一样,const指针也必须在定义的时候进行初始化。
指针本身是const的事实并没有说明是否能使用该指针修改它所指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. int _tmain(int argc, _TCHAR* argv[])  
  6. {  
  7.     int iValue = 1;  
  8.     int *const pNumber = &iValue;  
  9.     *pNumber = 2;  
  10.     return 0;  
  11. }</span>  


 

3)指向const对象的const指针
还可以如下定义指向const对象的const指针:
const double pNumble = 3.14;
const double *const pi_ptr = &pi;
这时,既不能修改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那样)

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4. #include "fstream"  
  5.   
  6.   
  7. int _tmain(int argc, _TCHAR* argv[])  
  8. {  
  9.     char automoblie[20];  
  10.     int year;  
  11.     double a_price;  
  12.     double d_price;  
  13.   
  14.     ofstream outFile;  
  15.     outFile.open("test.txt");  
  16.       
  17.     cout << "Enter the make and model of automobile:" << endl;  
  18.     cin.getline( automoblie, 20 );  
  19.     cout << "Enter the model year:" << endl;  
  20.     cin >> year;  
  21.     cout << "Enter the original asking price:" << endl;  
  22.     cin >> a_price;  
  23.     d_price = 0.96 * a_price;  
  24.       
  25.     cout << fixed;         // floating point numbers using a general way the output  
  26.     cout.precision( 2 ); // output decimal point behind 1 bits  
  27.     cout.setf( ios_base::showpoint ); // compulsory output decimal point  
  28.     cout << "make and model of automobile is:" << automoblie << endl;  
  29.     cout << "model year is:" << year << endl;  
  30.     cout << "was asking:" << a_price << endl;  
  31.     cout << "now asking:" << d_price << endl;  
  32.   
  33.     outFile << fixed;  
  34.     outFile.precision( 2 );  
  35.     outFile.setf( ios_base::showpoint );  
  36.     outFile << "make and model of automobile is:" << automoblie << endl;  
  37.     outFile << "model year is:" << year << endl;  
  38.     outFile << "was asking:" << a_price << endl;  
  39.     outFile << "now asking:" << d_price << endl;  
  40.   
  41.     outFile.close();  
  42.   
  43.     return 0;  
  44. }</span>  


读取文件
文件输出和文件输入极其类似
使用文件输出的步骤:
1)包含头文件fstream
2)创建ifstream对象inFile
3)inFile.is_open()
4)getline( inFile, strOut );

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4. #include "string"  
  5. #include "algorithm"  
  6. #include "fstream"  
  7. #include "vector"  
  8.   
  9. #define FILENAMELENGTH 20  
  10.   
  11. void Print( const string str)  
  12. {  
  13.     cout << str << endl;  
  14. }  
  15.   
  16. int _tmain(int argc, _TCHAR* argv[])  
  17. {  
  18.     vector<string> vecStr;  
  19.     char strFileName[ FILENAMELENGTH ];  
  20.     string strOut;  
  21.     cout << "Enter the data file:" << endl;  
  22.     cin >> strFileName;  
  23.   
  24.     ifstream inFile;  
  25.     inFile.open( strFileName );  
  26.     if ( !inFile.is_open() )  
  27.     {  
  28.         cout << "could not open the datafile" << strFileName << endl;  
  29.         cout << "Program terinating.\n";  
  30.         exit( EXIT_FAILURE );  
  31.     }  
  32.     while ( inFile.good() )  
  33.     {  
  34.         getline( inFile, strOut );  
  35.         vecStr.push_back( strOut );  
  36.     }  
  37.   
  38.     for_each( vecStr.begin(), vecStr.end(), Print );  
  39.   
  40.     inFile.close();  
  41.   
  42.   
  43.     return 0;  
  44. }  
  45. </span>  


如果试图打开一个不存在的文件,这将导致后面使用ifstream对象进行输入时失败。检查文件是否被成功打开的首先方法是使用方法is_open(),为此,可以用类似的代码:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">inFile.open( strFileName );  
  2. if ( !inFile.is_open() )  
  3. {  
  4.     cout << "could not open the datafile" << strFileName << endl;  
  5.     cout << "Program terinating.\n";  
  6.     exit( EXIT_FAILURE );  
  7. }</span>  


    如果文件被成功地打开,方法is_open()将返回true;因此如果文件没有被打开,表达式!inFile.is_open()将为true。函数exit()的原型在头文件cstdlib中定义,在该头文件中,还定义了一个用于同操作系统通信的参数值EXIT_FAILURE。函数exit()终止程序。
    使用good()方法,该方法在没有发生任何错误时返回true

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">  while ( inFile.good() )  
  2.     {  
  3.         getline( inFile, strOut );  
  4.         vecStr.push_back( strOut );  
  5.     }  
  6. </span>  


如果愿意,可以使用其它方法来确定循环种终止的真正原因:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">  if ( inFile.eof() )  
  2.     {  
  3.         cout << "End of the file reached.\n";  
  4.     }  
  5.     else if ( inFile.fail() )  
  6.     {  
  7.         cout << "Input terminated by data mismatch.\n";  
  8.     }  
  9.     else  
  10.     {  
  11.         cout << "Input terminated fo  
  12. r Unknown reason.\n";  
  13.     }  
  14. </span>  


 

 

数组作为函数参数
    当数组作为函数参数时,将数组的地址作为参数可以节省复制整个数组所需的时机和内存
验证一下,函数中数组的地址与外层调用时,函数的地址是一样的,并且在函数中

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. int sumArray( const int arr[], int nValue )  
  6. {  
  7.     int sum = 0;  
  8.     int nArrsize = 0;  
  9.     cout << "In sumArray function arr[] address:" << arr << endl;  
  10.     nArrsize = sizeof arr;  
  11.     cout << "In sumArray function sizeof arr is:" << nArrsize << endl;  
  12.     for ( int i = 0; i < nValue; i++ )  
  13.     {  
  14.         sum += arr[i];  
  15.     }  
  16.     return sum;  
  17. }  
  18. /* 
  19. int sumArray( const int* arr, int nValue ) 
  20. { 
  21.     int sum = 0; 
  22.     int nArrsize = 0; 
  23.     cout << "In sumArray function arr[] address:" << arr << endl; 
  24.     nArrsize = sizeof arr; 
  25.     cout << "In sumArray function sizeof arr is:" << nArrsize << endl; 
  26.     for ( int i = 0; i < nValue; i++ ) 
  27.     { 
  28.         sum += arr[i]; 
  29.     } 
  30.     return sum; 
  31. }*/  
  32. int _tmain(int argc, _TCHAR* argv[])  
  33. {  
  34.     int nArrsize = 0;  
  35.     int arr[ 5 ] = { 1, 2, 3, 4, 5 };  
  36.     cout << "In _tmain function arr[] address:" << arr << endl;  
  37.     nArrsize = sizeof arr;  
  38.     cout << "In _tmain function sizeof arr is:" << nArrsize << endl;  
  39.   
  40.     cout << "sum is:" << sumArray( arr, 5 ) << endl;  
  41.   
  42.     return 0;  
  43. }</span>  

 


指针和const
将const用于指针有一些很微妙的地方,可以用两种不同的方式将const关键字用于指针。
1)
第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     const int iValue = 10;  
  6.     int* pIValue = &iValue;  
  7.   
  8.     *pIValue = 11;  
  9.   
  10.     return 0;  
  11. }</span>  


2)
第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     int iValue = 10;  
  6.     const int* pIValue = &iValue;  
  7.   
  8.     *pIValue = 11;  
  9.   
  10.     return 0;  
  11. }</span>  


3)有这种情况:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     int iAge = 39;  
  6.     int* pIAge = &iAge;  
  7.     const int* pIValue = pIAge;  
  8.     *pIValue = 10;  
  9.   
  10.     return 0;  
  11. }</span>  


这是情况变得很复杂。假如涉及的是一级间接关系,则将非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)指向常量的常指针

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     int IValue = 10;  
  6.     const intconst pIValue = &IValue;  
  7. //  *pIValue = 11;  
  8.     int IAge = 39;  
  9.     pIValue = &IAge;  
  10.   
  11.     return 0;  
  12. }</span>  

这时既不能修改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 ]

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. int _tmain(int argc, _TCHAR* argv[])  
  6. {  
  7.     int arr2[ 2 ][ 4 ] = {  
  8.                             { 1, 2, 3, 4 },  
  9.                             { 5, 6, 7, 8 }  
  10.                           };  
  11.     cout << arr2[1][2] << endl;  
  12.     cout << arr2 << endl;  
  13.     int r = 1;  
  14.     cout << arr2 + r << endl;  
  15.     int c = 2;  
  16.     cout << *( arr2 + r ) << endl;  
  17.     cout << *( arr2 + r ) + c << endl;  
  18.     cout << *( ( arr2 + r) + c ) << endl;  
  19.     cout << *(*( arr2 + r ) + c ) << endl;  
  20.       
  21.     return 0;  
  22. }</span>  

 


递归
1)
仅包含一个递归的调用

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. // recutsion with iValue  
  6. int sum( int iValue )  
  7. {  
  8.     if ( 0 == iValue )  
  9.     {  
  10.         return 1;  
  11.     }  
  12.     else  
  13.     {  
  14.         return sum( iValue - 1 ) * iValue;  
  15.     }  
  16. }  
  17.   
  18. int _tmain(int argc, _TCHAR* argv[])  
  19. {  
  20.     cout << " 5 recursion:" << sum( 5 ) << endl;  
  21.   
  22.     return 0;  
  23. }  
  24. </span>  


此代码起警示作用:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. // recutsion with iValue  
  6. int sum( int iValue )  
  7. {  
  8.     if ( 0 == iValue )  
  9.     {  
  10.         return 1;  
  11.     }  
  12.     else  
  13.     {  
  14.         return sum( iValue-- ) * iValue;  
  15.     }  
  16. }  
  17.   
  18. int _tmain(int argc, _TCHAR* argv[])  
  19. {  
  20.     cout << " 5 recursion:" << sum( 5 ) << endl;  
  21.   
  22.     return 0;  
  23. }</span>  



2)
包含多个递归调用的递归,在需要将一项工作不断分为两项较小的、类似的工作时,递归非常有用。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. const int Len = 66;  
  6. const int Divs = 6;  
  7.   
  8. void subdivide( char arr[], int low, int high, int level )  
  9. {  
  10.     if ( 0 == level )  
  11.     {  
  12.         return;  
  13.     }  
  14.     int mid = ( low + high ) / 2;  
  15.     arr[ mid ] = '|';  
  16.     subdivide( arr, low, mid, level - 1);  
  17.     subdivide( arr, mid, high, level - 1);  
  18. }  
  19.   
  20. int _tmain(int argc, _TCHAR* argv[])  
  21. {  
  22.     char ruler[ Len ] = { 0 };  
  23.     int i;  
  24.     // initialize  
  25.     for ( i = 1; i <= Len - 2; i++ )  
  26.     {  
  27.         ruler[ i ] = ' ';  
  28.     }  
  29.     ruler[ Len - 1 ] = '\0';    // present end  
  30.     int iMax = Len - 2;         // 64min length is Len - 2  
  31.     int iMin = 0;               // set min length is 0  
  32.     ruler[ iMin ] = ruler[ iMax ] = '|'// min and max pos now is '|'  
  33.     cout << ruler << endl;  // output none but have min and max  
  34.     // cout 6 row  
  35.     for ( i = 1; i <= Divs; i++ )  
  36.     {  
  37.         subdivide( ruler, iMin, iMax, i); // transfer i  
  38.         cout << ruler << endl;  
  39.         // resume array is NULL  
  40.         for ( int j = i; j < Len - 2; j++ )  
  41.         {  
  42.             ruler[ j ] = ' ';  
  43.         }  
  44.     }  
  45.     return 0;  
  46. }  
  47. </span>  


 

在subdivide()函数,使用便利level来控制递归层。subdivide()函数调用自己两次,一次针对左半部分,另一次针对右半部分。也就是说,调用一次导致两个调用,然后导致4个调用,在导致8个调用,以此类推。这就是6层调用能够填充64个元素的原因pow( 2, 6 )=64。这将不断导致函数调用数(以及存储的变量数)翻倍,因此如果要求的递归层次很多,这种递归方式将是一种糟糕的选择;然而,如果递归层次较少,这将是一种精致而简单的选择。

 

函数指针
历史与逻辑
为何pf和(*pf)等家呢?一种学派认为,由于pf是函数指针,而*pf是函数,因此应将(*pf)()用作函数调用;另一种学派认为,由于函数名师指向该函数的指针,指向函数的指针的行为应与函数名相似,因此应将pf()用作函数调用使用。C++进行了折中----这两种方式都是正确的,或者至少是允许的,虽然它们在逻辑上是相互冲突的。在认为折中折中粗糙之前,应该想远类思维活动的特点。

函数指针示例:
1)使用typedef

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. double betsy( int );  
  6. double pam( int );  
  7.   
  8. typedef double (*estimate)( int );  
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     int iValue = 5;  
  13.     estimate estimateFun;  
  14.   
  15.     estimateFun = betsy;  
  16.     cout << "transfer betsy:" << estimateFun( iValue ) << endl;  
  17.     estimateFun = pam;  
  18.     cout << "transfer pam:"   << estimateFun( iValue ) << endl;  
  19.     return 0;  
  20. }  
  21.   
  22. double betsy( int iValue )  
  23. {  
  24.     return ( iValue * iValue );  
  25. }  
  26. double pam( int iValue )  
  27. {  
  28.     return ( iValue * 0.89 );  
  29. }</span>  


2)直接使用

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. double betsy( int );  
  6. double pam( int );  
  7.   
  8. double estimateFun( int iValue, double ( *pf )(int) );  
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     int iValue = 5;  
  13.     cout << "transfer betsy:" << estimateFun( iValue, betsy) << endl;  
  14.     cout << "transfer pam:"   << estimateFun( iValue,pam ) << endl;  
  15.     return 0;  
  16. }  
  17.   
  18. double betsy( int iValue )  
  19. {  
  20.     return ( iValue * iValue );  
  21. }  
  22. double pam( int iValue )  
  23. {  
  24.     return ( iValue * 0.89 );  
  25. }  
  26. double estimateFun( int iValue, double ( *pf )(int) )  
  27. {  
  28.     return ( *pf )( iValue );  
  29. }</span>  



C++函数
1)内联函数
    常规函数:在执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码,然后跳回到地址被保存的指令处。来回跳跃并记录跳跃位置以为着使用函数时,需要一定的开销。
    C++内联函数提供了另一种选择。内联汗水的编译代码与其他程序代码"内联"起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无须跳到另一个位置跳到另一个位置处执行代码,然后再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。如果程序在10个不同的地方调用同一个内联函数,则该程序将包含该函数的10个代码拷贝。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. inline int sum( int iLeftValue, int iRightValue )  
  6. {  
  7.     return ( iLeftValue + iRightValue );  
  8. }  
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     int a = 10;  
  13.     int b = 14;  
  14.     cout << sum( a , b ) << endl;  
  15.   
  16.     return 0;  
  17. }</span>  


2)引用作为函数的参数
引用作为某个变量的别名而存在
他与指针的区别在于,声明的时候就必须进行初始化

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. int _tmain(int argc, _TCHAR* argv[])  
  6. {  
  7.     int a = 14;  
  8.     int b = 10;  
  9.     int& ra = a;  
  10.     cout << "current a value is:" << a << endl;  
  11.     cout << "current reference ra value is:" << ra << endl;  
  12.     ra++;  
  13.     cout << "current a value is:" << a << endl;  
  14.     cout << "current reference ra value is:" << ra << endl;  
  15.     ra = b;  
  16.     cout << "current b value is:" << b << endl;  
  17.     cout << "current reference ra value is:" << ra << endl;  
  18.   
  19.     return 0;  
  20. }</span>  


将引用作为函数的参数,以达到不进行按值传递的目的

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. void sum( int& a )  
  6. {  
  7.     a++;  
  8. }  
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     int a = 14;  
  13.     cout << "current a value is:" << a << endl;  
  14.     sum( a );  
  15.     cout << "current a value is:" << a << endl;  
  16.   
  17.     return 0;  
  18. }</span>  


 

3)对象、继承、引用
    简单的说,ostream是基类,而ofstream是派生类。派生类继承了基类的方法,这意味着ofstream对象可以使用基类的特性,如格式化方法precision()和self()
    继承的另一个特征是基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。例如,参数类型为ostream&的函数可以接受ostream对象(如cout)或ofstream对象作为参数

4)何时使用引用参数
a.程序员能够修改调用函数中的数据对象
b.通过传递引用而不是整个数据对象,可以提高程序的运行速度
5)何时使用按值传递
a.如果数据对象很小,如内置数据类型或小型结构,则按值传递
b.如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针
c.如果数据对象时较大的结构,则使用const指针或const引用,以提高程序的效率,这样可以节省复制结构所需的时间和空间
d.如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标准方式是按引用传递


5)函数重载
参数个数不同构成的重载

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. void sum( int iLeftValue, int iRightValue )  
  6. {  
  7. }  
  8. void sum( int iLeftValue, int iMidValue, int iRightValue )  
  9. {  
  10. }</span>  


参数类型不同构成的重载

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. void sum( int iLeftValue, int iRightValue )  
  6. {  
  7. }  
  8. void sum( double fLeftValue, double fMidValue )  
  9. {  
  10. }</span>  

其他的比如,返回值类型,不能区别一个函数


6)函数模板
函数模板是通用的函数描述,也就是说它们使用通用类型来定义函数,其中的通用类型可用具体的类型(如int或double)替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。由于模板允许通用类型(而不是具体类型)的方式编写程序,因此有时也被称为通用编程。由于类型是用参数表示的,因此模板特性有时也被称为参数化类型(parametarized types)。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. /* 
  6. template<class T> 
  7. void Swap( T* a, T* b ) 
  8. { 
  9.     T temp; 
  10.     temp = *a; 
  11.     *a = *b; 
  12.     *b = temp; 
  13. }*/  
  14. template<class T>  
  15. void Swap( T& a, T& b )  
  16. {  
  17.     T temp;  
  18.     temp = a;  
  19.     a = b;  
  20.     b = temp;  
  21. }  
  22.   
  23. int _tmain(int argc, _TCHAR* argv[])  
  24. {  
  25.     int ia = 14;  
  26.     int ib = 10;  
  27.     cout << "current ia value is:" << ia << endl;  
  28.     cout << "current b value is:" << ib << endl;  
  29.     Swap<int>( ia, ib );  
  30.     cout << "current ia value is:" << ia << endl;  
  31.     cout << "current ib value is:" << ib << endl;  
  32.   
  33.     cout << "\n";  
  34.     double fa = 14.4;  
  35.     double fb = 10.4;  
  36.     cout << "current fa value is:" << fa << endl;  
  37.     cout << "current fb value is:" << fb << endl;  
  38.     Swap<double>( fa, fb );  
  39.     cout << "current fa value is:" << fa << endl;  
  40.     cout << "current fb value is:" << fb << endl;  
  41.   
  42.     return 0;  
  43. }</span>  


a、重载的函数模板
需要多个对不同类型使用同一种算法的函数时,可使用模板。不过,并非所有的类型都使用相同的算法。为满足这种需求,可以像重载常规函数定义那样重载函数模板定义。和常规重载一样,被重载的模板的函数特征为( T&, T& )。而新模板的特征为( T[], T[], int ),最后一个参数的类型为具体类型( int ),而不是通用类型。并非所有的模板参数都必须是模板参数类型。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. #define ARRAYMAXLENGTH  4  
  6.   
  7. template<class T>  
  8. void Swap( T& a, T& b )  
  9. {  
  10.     T temp;  
  11.     temp = a;  
  12.     a = b;  
  13.     b = temp;  
  14. }  
  15.   
  16. template<class T>  
  17. void Swap( T arr[], T brr[], const int nLength )  
  18. {  
  19.     T temp;  
  20.     for ( int i = 0; i < nLength; i++ )  
  21.     {  
  22.         temp = arr[ i ];  
  23.         arr[ i ] = brr[ i ];  
  24.         brr[ i ] = temp;  
  25.     }  
  26. }  
  27.   
  28. template<class T>  
  29. void DisplayArray( const T arr[], int nLength )  
  30. {  
  31.     for ( int i = 0; i < nLength; i++ )  
  32.     {  
  33.         cout << "current " << i << " value is:" << arr[ i ] << endl;  
  34.     }  
  35. }  
  36.   
  37. int _tmain(int argc, _TCHAR* argv[])  
  38. {  
  39.     int ia = 14;  
  40.     int ib = 10;  
  41.     cout << "current ia value is:" << ia << endl;  
  42.     cout << "current b value is:" << ib << endl;  
  43.     Swap<int>( ia, ib );  
  44.     cout << "current ia value is:" << ia << endl;  
  45.     cout << "current ib value is:" << ib << endl;  
  46.   
  47.     cout << "\n";  
  48.     int arr[] = { 1, 2, 3, 4 };  
  49.     int brr[] = { 9, 8, 7, 6 };  
  50.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  51.     Swap<int>( arr, brr, ARRAYMAXLENGTH );  
  52.     cout << "\n";  
  53.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  54.   
  55.     return 0;  
  56. }  
  57. </span>  


 

b、模板的显示具体化
对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及他们的重载版本。
显示具体化的圆形和定义应以template<>打头,并通过名称来指出类型。
具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. #define ARRAYMAXLENGTH  4  
  6.   
  7. template<class T>  
  8. void Swap( T arr[], T brr[], const int nLength )  
  9. {  
  10.     T temp;  
  11.     for ( int i = 0; i < nLength; i++ )  
  12.     {  
  13.         temp = arr[ i ];  
  14.         arr[ i ] = brr[ i ];  
  15.         brr[ i ] = temp;  
  16.     }  
  17. }  
  18. template<>  
  19. void Swap( double arr[], double brr[], const int nLength)  
  20. {  
  21.     double temp;  
  22.     for ( int i = 0; i < nLength; i++ )  
  23.     {  
  24.         temp = arr[ i ];  
  25.         arr[ i ] = brr[ i ];  
  26.         brr [ i ] = temp;  
  27.     }  
  28.     cout << "enter in this function!" << endl;  
  29. }  
  30. template<class T>  
  31. void DisplayArray( const T arr[], int nLength )  
  32. {  
  33.     for ( int i = 0; i < nLength; i++ )  
  34.     {  
  35.         cout << "current " << i << " value is:" << arr[ i ] << endl;  
  36.     }  
  37. }  
  38.   
  39. int _tmain(int argc, _TCHAR* argv[])  
  40. {  
  41.     cout << "\n";  
  42.     int arr[] = { 1, 2, 3, 4 };  
  43.     int brr[] = { 9, 8, 7, 6 };  
  44.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  45.     Swap<int>( arr, brr, ARRAYMAXLENGTH );  
  46.     cout << "\n";  
  47.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  48.   
  49.     cout << "\n";  
  50.     double dArr[] = { 1.1, 2.2, 3.3, 4.4 };  
  51.     double dBrr[] = { 9.9, 8.8, 7.7, 6.6 };  
  52.     DisplayArray<double>( dArr, ARRAYMAXLENGTH );  
  53.     Swap<double>( dArr, dBrr, ARRAYMAXLENGTH );  
  54.     cout << "\n";  
  55.     DisplayArray<double>( dArr, ARRAYMAXLENGTH );  
  56.   
  57.     return 0;  
  58. }</span>  


c、非模板函数和模板函数共存
如果不是对模板进行实例化,比如:
Swap<double>( dArr, dBrr, ARRAYMAXLENGTH );
的调用,那么调用模板函数,如果调用形式是Swap( dArr, dBrr, ARRAYMAXLENGTH );,则优先调用非模板函数

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. #define ARRAYMAXLENGTH  4  
  6.   
  7. template<class T>  
  8. void Swap( T arr[], T brr[], const int nLength )  
  9. {  
  10.     T temp;  
  11.     for ( int i = 0; i < nLength; i++ )  
  12.     {  
  13.         temp = arr[ i ];  
  14.         arr[ i ] = brr[ i ];  
  15.         brr[ i ] = temp;  
  16.     }  
  17. }  
  18.   
  19. template<>  
  20. void Swap( double arr[], double brr[], const int nLength)  
  21. {  
  22.     double temp;  
  23.     for ( int i = 0; i < nLength; i++ )  
  24.     {  
  25.         temp = arr[ i ];  
  26.         arr[ i ] = brr[ i ];  
  27.         brr [ i ] = temp;  
  28.     }  
  29.     cout << "enter in this function1!" << endl;  
  30. }  
  31.   
  32. void Swap( double arr[], double brr[], const int nLength)  
  33. {  
  34.     double temp;  
  35.     for ( int i = 0; i < nLength; i++ )  
  36.     {  
  37.         temp = arr[ i ];  
  38.         arr[ i ] = brr[ i ];  
  39.         brr [ i ] = temp;  
  40.     }  
  41.     cout << "enter in this function2!" << endl;  
  42. }  
  43.   
  44. template<class T>  
  45. void DisplayArray( const T arr[], int nLength )  
  46. {  
  47.     for ( int i = 0; i < nLength; i++ )  
  48.     {  
  49.         cout << "current " << i << " value is:" << arr[ i ] << endl;  
  50.     }  
  51. }  
  52.   
  53. int _tmain(int argc, _TCHAR* argv[])  
  54. {  
  55.     cout << "\n";  
  56.     int arr[] = { 1, 2, 3, 4 };  
  57.     int brr[] = { 9, 8, 7, 6 };  
  58.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  59.     Swap<int>( arr, brr, ARRAYMAXLENGTH );  
  60.     cout << "\n";  
  61.     DisplayArray<int>( arr, ARRAYMAXLENGTH );  
  62.   
  63.     cout << "\n";  
  64.     double dArr[] = { 1.1, 2.2, 3.3, 4.4 };  
  65.     double dBrr[] = { 9.9, 8.8, 7.7, 6.6 };  
  66.     DisplayArray<double>( dArr, ARRAYMAXLENGTH );  
  67.     Swap( dArr, dBrr, ARRAYMAXLENGTH );  
  68.     cout << "\n";  
  69.     DisplayArray<double>( dArr, ARRAYMAXLENGTH );  
  70.   
  71.     return 0;  
  72. }</span>  

 


限定符volatile、mutable
    volatile关键字表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。例如,可以将一个指针指向某个硬件的位置,其实包含了来自串行端口的时间或信息。在这种情况下,硬件(而不是程序)了能修改其中的内容。或者两个程序可能互相影响,共享数据。该关键字的作用是为了改善编译器的优化能力。例如,假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中。这种优化假设变量的值在这两次使用之间不会发生变化。如果不将变量声明为volatile,则编译器将进行这种优化;将变量声明为volatile,相当于告诉编译器,不要进行这种优化。
    mutable,可以用它来指出,即使结构(或类)变量为const,其某个成员也可以被修改。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. struct Student   
  6. {  
  7.     mutable int iAge;  
  8.     mutable char szName[ 10 ];  
  9. };  
  10.   
  11. int _tmain(int argc, _TCHAR* argv[])  
  12. {  
  13.     const Student stu = { 23, "zengraoli" };  
  14.     cout << "current student age is:" << stu.iAge << endl;  
  15.     cout << "current student name is:" << stu.szName << endl;  
  16.   
  17.     stu.iAge = 24;  
  18.     memcpy( stu.szName, "zeng"sizeof("zeng") );  
  19.     cout << "\n";  
  20.     cout << "current student age is:" << stu.iAge << endl;  
  21.     cout << "current student name is:" << stu.szName << endl;  
  22.   
  23.   
  24.     return 0;  
  25. }</span>  

 


显示指出调用约定
在C语言中,一个名称只对应一个函数,因此这很容易实现。因此,为满足内部需要,C语言编译器可能将max这样的函数名翻译成_max。这种方法被称为C语言链接性(C language linkage)。但在C++中,同一个名称可能对应多个函数,必须将这些函数翻译为不同的符号名称。因此,C++编译器执行名称纠正或名称修饰,为重载函数生成不同的函数名称。例如,可能将max2( int,int )转换成_max2_i_i,而将max2( double, double )转换成_max_d_d。这种方法称为C++语言链接。
当然,可以显示的指出调用约定:

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">extern "C" int max( int a, int b )  
  2. {  
  3.     return ( (a > b ) ? a : b );  
  4. }  
  5.   
  6. extern "C++" int max2( int a, int b )  
  7. {  
  8.     return ( (a > b ) ? a : b );  
  9. }</span>  

 


名称空间
1)未命名的名称空间
可以通过省略名称空间的名称来创建未命名的名称空间
namespace
{
 int iValue;
 int IAge;
}
这就像后面跟着using编译指令一样,也就是说,在该名称空间中声明的名称潜在作用于为:从声明点到该声明区域末尾。从这个方面看,他们与全局变量相似。不过,由于这种名称空间没有名称,因此不能显示地使用using编译指令或using声明来使它在其他位置都可用。具体地说,不能在未命名名称空间所属文件之外的其他文件中,使用该名称空间中的名称,因此这种方法可以替代链接性为内部的静态变量。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2.   
  3. namespace  
  4. {  
  5.     int iValue;  
  6.     int IAge;  
  7. }  
  8.   
  9. int _tmain(int argc, _TCHAR* argv[])  
  10. {  
  11.     iValue = 14;  
  12.     IAge = 39;  
  13.   
  14.     return 0;  
  15. }</span>  


 

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)可查看堆栈是否为空
可以将上述描述转换为一个类声明,其中公有成员函数提供了表示堆栈操作的接口,而私有数据成员负责存储堆栈数据。

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">// testStack.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "iostream"  
  6. using namespace std;  
  7.   
  8. typedef int MyType;  
  9.   
  10. templatetypename Item >  
  11. class Stack  
  12. {  
  13. public:  
  14.     Stack()  
  15.     {  
  16.         top = 0;  
  17.         memset( data, 0, sizeof( data ) );  
  18.     }  
  19.     bool Push( const Item item )  
  20.     {  
  21.         if ( MAX == top )  
  22.         {  
  23.             return false;  
  24.         }  
  25.         data[ top++ ] = item;  
  26.         return true;  
  27.     }  
  28.     bool Pop( Item &item )  
  29.     {  
  30.         if ( 0 == top )  
  31.         {  
  32.             return false;  
  33.         }  
  34.         item = data[ --top ];  
  35.         return true;  
  36.     }  
  37.     bool IsEmpty()  
  38.     {  
  39.         return ( 0 == top );  
  40.     }  
  41.     bool IsFull()  
  42.     {  
  43.         return ( MAX == top );  
  44.     }  
  45.     void Print()  
  46.     {  
  47.         for ( int i = 0; i < top; i++ )  
  48.         {  
  49.             cout << "the " << i << " value is:" << data[ i ] << endl;  
  50.         }  
  51.     }  
  52. private:  
  53.     enum { MAX = 10 };  
  54.     Item data[ MAX ];  
  55.     int top;  
  56. };  
  57.   
  58. int _tmain(int argc, _TCHAR* argv[])  
  59. {  
  60.     int i;  
  61.     MyType temp;  
  62.     Stack<MyType> test;  
  63.     cout << "isEmpty:" << test.IsEmpty() << endl;  
  64.   
  65.     for ( i = 0; i <= 9; i++ )  
  66.     {  
  67.         test.Push( i + 1 );  
  68.     }  
  69.     cout << "isFull:" << test.IsFull() << endl;  
  70.   
  71.     if ( !test.Push( 11 ) )  
  72.     {  
  73.         cout << "push failure!" << endl;  
  74.     }  
  75.       
  76.     for ( i = 0; i <= 9; i++ )  
  77.     {  
  78.         if ( test.Pop( temp ))  
  79.         {  
  80.             cout << "pop a elem:" << temp << endl;  
  81.         }  
  82.     }  
  83.       
  84.     if ( !test.Push( 11 ) )  
  85.     {  
  86.         cout << "push failure!" << endl;  
  87.     }  
  88.     test.Print();  
  89.   
  90.     return 0;  
  91. }</span>  

 


使用类
1)类中一个加法操作符的重载例子

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "string"  
  3. #include "iostream"  
  4. using namespace std;  
  5.   
  6. namespace  
  7. {  
  8.     class CTest_A  
  9.     {  
  10.     public:  
  11.         CTest_A( int nValue, string strName )  
  12.         {  
  13.             m_nAge = nValue;  
  14.             m_strName = strName;  
  15.         }  
  16.         // override operator +  
  17.         CTest_A operator +( const CTest_A& rCTest_A ) const  
  18.         {  
  19.             m_nAge += rCTest_A.m_nAge;  
  20.             return *this;  
  21.         }  
  22.         ~CTest_A(){}  
  23.         inline int GetAge() const  
  24.         {  
  25.             return m_nAge;  
  26.         }  
  27.         inline string GetName() const  
  28.         {  
  29.             return m_strName;  
  30.         }  
  31.     private:  
  32.         int m_nAge;  
  33.         string m_strName;  
  34.     };  
  35. }  
  36.   
  37. int _tmain(int argc, _TCHAR* argv[])  
  38. {  
  39.     CTest_A CA( 23, "zengraoli" );  
  40.     CTest_A CB( 23, "zengraoli2" );  
  41.     CB = CB +CA;  
  42.   
  43.     cout << "current student name is:" << CB.GetName() << endl;  
  44.     cout << "current student age is:" << CB.GetAge() << endl;  
  45.   
  46.     return 0;  
  47. }</span>  


 

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;

[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#include "stdafx.h"  
  2. #include "iostream"  
  3. using namespace std;  
  4.   
  5. namespace  
  6. {  
  7.     class CTime  
  8.     {  
  9.     public:  
  10.         CTime( int nHours = 0, int nMiniutes= 0 )   
  11.             :m_nHours( nHours ), m_nMiniutes( nMiniutes )  
  12.         {  
  13.         }  
  14.     friend CTime operator*( double ftime, const CTime& ct );  
  15.     inline int GetHours() const  
  16.     {  
  17.         return m_nHours;  
  18.     }  
  19.     inline int GetMiniute() const  
  20.     {  
  21.         return m_nMiniutes;  
  22.     }  
  23.     private:  
  24.         int m_nHours;  
  25.         int m_nMiniutes;  
  26.     };  
  27.     CTime operator*( double ftime, const CTime& ct )  
  28.     {  
  29.         CTime result;  
  30.         long totalminutes = static_cast<long>( ct.m_nHours * ftime * 60 + ct.m_nMiniutes * ftime );  
  31.         result.m_nHours = totalminutes / 60;  
  32.         result.m_nMiniutes = totalminutes % 60;  
  33.         return result;  
  34.     }  
  35. }  
  36. int _tmain(int argc, _TCHAR* argv[])  
  37. {  
  38.     CTime CA( 4, 10 );  
  39.     CA = 2.0 * CA;  
  40.     cout << "current hours is:" << CA.GetHours() << "  " << "current miniutes is:" << CA.GetMiniute() << endl;  
  41.   
  42.     return 0;  
  43. }</span>  


4)常用的友元:重载<<操作符
    一个很有用的类特性是,可以对<<操作符进行重载,使之能与cout一起来显示对象的内容。当输出time对象的时候,可以直接使用cout << time;之所以可以这样做,是因为<<是可被重载的C++操作符之一。实际上,它已经被重载很多次了。最初,他表示额含义是按位移。ostream类对该操作符进行了重载,将其转换为一个输出工具。
    在重载<<的时候应使用cout对象本身(void operator<<( ostream& os, CTime& ct )),而不是他的拷贝。因此该函数按应用(而不是按值)来传递该对象。这样,表达式cout << time;将导致os成为cout的一个别名;而表达式cout << time;将导致os成为cerr的另一个别名。

[cpp] view plaincopyprint?
 
  1. #include "iostream"  
  2. using namespace std;  
  3.   
  4. namespace  
  5. {  
  6.     class CTime  
  7.     {  
  8.     public:  
  9.         CTime( int nHours = 0, int nMiniutes= 0 )   
  10.             :m_nHours( nHours ), m_nMiniutes( nMiniutes )  
  11.         {  
  12.         }  
  13.     friend CTime operator*( double ftime, const CTime& ct );  
  14.     inline int GetHours() const  
  15.     {  
  16.         return m_nHours;  
  17.     }  
  18.     inline int GetMiniute() const  
  19.     {  
  20.         return m_nMiniutes;  
  21.     }  
  22.     private:  
  23.         int m_nHours;  
  24.         int m_nMiniutes;  
  25.     };  
  26.     CTime operator*( double ftime, const CTime& ct )  
  27.     {  
  28.         CTime result;  
  29.         long totalminutes = static_cast<long>( ct.m_nHours * ftime * 60 + ct.m_nMiniutes * ftime );  
  30.         result.m_nHours = totalminutes / 60;  
  31.         result.m_nMiniutes = totalminutes % 60;  
  32.         return result;  
  33.     }  
  34.     ostream& operator<<( ostream& os, CTime& ct )  
  35.     {  
  36.         os << "current hours is:" << ct.GetHours() << "  " << "current miniutes is:" << ct.GetMiniute() << endl;  
  37.         return os;  
  38.     }  
  39. }  
  40.   
  41.   
  42. int _tmain(int argc, _TCHAR* argv[])  
  43. {  
  44.     CTime CA( 4, 10 );  
  45.     CA = 2.0 * CA;  
  46. //  cout << "current hours is:" << CA.GetHours() << "  " << "current miniutes is:" << CA.GetMiniute() << endl;  
  47.     cout << CA << CA;  
  48.     return 0;  
  49. }
0 0
原创粉丝点击