1. 程式人生 > >C++ Primer Plus的若干收穫--(三)

C++ Primer Plus的若干收穫--(三)

有時候懷疑真是懷疑自己走的路到底是不是正確的。作為一個土生土長數學系學生,卻對數學毫無興趣,沒事的時候就喜歡躲在圖書館看看有關計算機的書。有時候期末考試時候會掛個一兩門的數學專業課,有時候真希望數學課本上這一個個繁瑣的證明是用程式碼寫的。自己幾乎丟掉了本專業的一切,去全身心投入到計算機這一龐大而繁瑣的學問中去。真心不知道2年之後自己畢業,自己的結果究竟是什麼樣的。自己現在唯一能做而又不讓自己後悔的我想就是儘可能的多學兩門語言與多看幾本計算機上的名著了。有時候真的不確定自己會堅持到什麼地步,是一直的走下去,還是。。。哎,先不糾結了,默默的堅持下去吧,堅持自己喜歡的東西。如果自己一直在數學的道路走下去的話,可能會生不如死。。。

3.1 陣列的替代品  模板類array

vector類的功能比陣列強大,但是付出的代價是效率稍低。如果您需要長度固定的陣列,使用陣列是最佳的選擇,但代價是不那麼方便和安全。有鑑於此,C++新增了模板類array。與陣列一樣array物件的長度也是固定的,也使用棧,而不是自由儲存區,因此效率與陣列相同

#include<array>
...
using namespace std;
array<int,5>ai;
array<double,4>ad={1.2,2.1,3.43.4.3};
//推而廣之
array<typename,n_element>array;

3.2 比較陣列、vector物件和array物件

下面來看一個簡單的示例

#include<iostream>
#include<vector>
#include<array>
int main()
{
	using namespace std;
	double a1[4]={1.2,2.4,3.6,4.8};

	vector<double>a2(4);
	a[0]=1/3;
	a[1]=2/3;
	a[3]=3/4'
	a[4]=4/5;

	array<double,4>a3={3.14,2.72,1.62,1.41};
	array<double,4>a4;
	a4=a3;//可以直接複製,區別於陣列
	
	cout<<"a1[2]:"<<a1[2]<<"at"<<&a1[2]<<endl;
	cout<<"a2[2]:"<<a2[2]<<"at"<<&a2[2]<<endl;
	cout<<"a3[2]:"<<a3[2]<<"at"<<&a3[2]<<endl;
	cout<<"a4[2]:"<<a4[2]<<"at"<<&a4[2]<<endl;
	return 0;
}

首先,注意到無論是陣列還是vector物件和array物件,都可以使用標準陣列表示法來訪問各個元素。其次從地址可知,array物件和陣列儲存在相同的記憶體區域(棧)中,而vector則儲存在另一個區域中,自由儲存區或堆中。第三,注意到可以將array物件賦給另一個array物件;而對於陣列,必須逐個複製元素。

3.3 副作用和順序點

首先副作用指的是計算表示式時對某些東西進行了修改;順序點是程式執行的一個點,在這裡,進去下一步之前將確保對所有的副作用進行評估。在C++中,語句中的分號就是一個順序點,這意味著程式在處理下一個語句之前,賦值運算子、遞增運算子和遞減運算子執行的所有修改都必須完成。另外任何完整的表示式末尾都是一個順序點。什麼是一個完整的表示式呢?他是這樣的:不是另一個更大表達式的子表示式。下面來通過兩個例子來加深一下理解

while(guests++<10)
  cout<<guests<<endl;
//在這裡表示式guests++<10是一個完整的表示式,因為它是while迴圈的測試條件,因此該表示式末尾是一個順序點

y=(4+x++)+(6+x++);
//表示式4+x++不是一個完整的表示式,因此不能保證計運算元表示式4+x++後立刻增加。在這裡整條賦值語句才是一個完整的表示式,而且分號表示了順序點,所以在這裡只保證在執行下一條語句之前才會將X增加兩次。

3.4 遞增/遞減和指標

當我們同時將*與++同時用於指標時將有這樣的疑問:將什麼解引用,將什麼遞增。這取決與優先順序和運算子的位置。字首遞增、字首遞減和解除引用運算子的優先順序相同。字尾遞增與字尾遞減的優先順序相同,但比字首運算子的優先順序高,這兩個運算子從左到右的方式進行結合。

  • 字首運算子從右向左結合規則意味著*++p的含義如下:先將++應用於pt,然後將*用於被遞增後的pt。另一方面,++*p意味著先去得pt指向的值,然後將這個值加一。
  • 接下來請看下面的組合(*pt)++。圓括號指出首先對指標解除引用,然後再將這個值遞增。
  • 最後再看下這個組合,x=*pt++;字尾運算子++的優先順序更高,這意味著將運算子用於pt,而不是*pt。

3.5 複合語句(語句塊)

語句塊就是用一對花括號括起來的一條複合語句,他被視作一條語句,從而滿足句法的要求。比如for迴圈:

for(int i=1;i<=5;i++)
{
   cout<<"Value"<<i<<":";
   cin>>number;
   sum+=number;
}
複合語句還有一條有趣的性質。如果在語句塊中定義一個新的變數,則僅當程式執行該語句塊時,該變數才存在。執行完語句塊後該變數將被釋放。如果在一個語句塊中宣告一個變數,而在外部語句中也有一個這樣的變數,情況將如何呢?在宣告位置到內部語句結束的範圍內,新變數將隱藏舊變數;然後舊變數再次可見,如下例所示:
#include<iostream>
int main()
{
	using namespace std;
	int  x=20;
	{
		cout<<x<<endl;
		int x=100;
		cout<<x<<endl;//use new x
	}
	cout<<x;// use original x
	return 0;
}

3.6 C-風格字串與string類字串的比較

首先討論下C-風格字串。由於C++將C-風格字串視為地址,因此如果以關係運算符來比較它們將無法得到滿意的結果。相反,應使用C-風格字串庫中的strcmp()函式。該函式接受兩個字串地址作為引數,這意味著引數可以是指標、字串或是陣列名。

下面著重介紹一下string類字串的比較。它的比較比較簡單,因為類設計能夠讓您使用關係運算符進行比較。這之所以可行是因為類函式過載了這些運算子。來看如下例子:

#include<iostream>
#include<string>
int main()
{
	using namespace std;
	string word="?ate";
	for(char ch='a';word!="mate";ch++)
	{
		cout<<word<<endl;
		word[0]=ch;//用陣列的方法來提取其中的字元
	}
	cout<<"After loop ends,words is"<<wrd<<endl;
	return 0;
}

  • string類過載運算子!=的方式能夠讓您在下述條件下使用它:至少有一個運算元為string類物件,另一個運算元可以是string類,也可以是C-風格字串;
  • string類的設計能夠將其作為一個實體,也可以將其作為一個聚合物件,從而用陣列的方法來提取其中的字元。
3.7 等待一段時間 :完成延時的迴圈

有時候延時一段時間很有用。比如,讀者可能遇到這樣的程式,它在螢幕上顯示一條訊息,而還沒來得及閱讀,又出現了其他的內容。C++庫中有一個函式有助於完成這樣的操作。這個函式名為clock,返回程式開始執行後所用的系統時間。這有兩個複雜的問題:clock()返回的不一定是秒;其次該函式的返回型別可能不確定。

為解決這樣的問題,在標頭檔案ctime提供了這樣的解決方案。首先它定義了一個符號常量--CLOCK PER SEC,該常量等於每秒鐘包含系統時間的單位數。因此將系統時間除以這個數可以得到秒數。或者將這個數乘上CLOCK PER SEC,可以得到以系統時間為單位的單位時間。其次,ctime將clock_t作為clock()返回型別的別名。如下程式演示瞭如何使用clock()函式和ctime標頭檔案來建立延時迴圈:

#include <iostream>
#include <ctime>

using namespace std;

int main()
{
    cout<<"Enter the delay time,in seconds:";
    float seconds;
    cin>>seconds;
    clock_t delay=seconds*<strong>CLOCKS_PER_SEC</strong>;
    cout<<"starting\a\n";
    clock_t start=clock();
    while(clock()-start<delay)
        ;
    cout<<"Done\a\n";
    return 0;
}


3.8 類型別名

下面簡要介紹一下類型別名。C++為型別建立別名有兩種方式。

一種是使用前處理器:

#define BYTE char
這樣,前處理器將在編譯程式編譯程式是用char來替換BYTE,從而使BYTE成為char的別名。

第二種方法使用關鍵字typedef來建立別名,完成上述的程式碼可以

typedef char BYTE;

typedef typeName aliaName//通用格式
一般情況下最好使用typedef來宣告一系列的變數,而在使用#define時可能會遇到一些問題。注意,typedef不會建立新型別,而只是為已有的型別建立一個別名,在有些情況下,在定義一些變數時會更簡便。