官术网_书友最值得收藏!

1.3 C++對C語言的擴充

C++在C語言的基礎上增加了很多新特性,例如,命名空間、bool類型等。本節將針對C++對C語言的擴充特性進行詳細講解。

1.3.1 命名空間

命名空間是C++語言的新特性,它能夠解決命名沖突問題。例如,小明定義了一個函數swap(),C++標準程序庫中也存在一個swap()函數。此時,為了區分調用的是哪個swap()函數,可以通過命名空間進行標識。

C++中的命名空間包括兩種,具體介紹如下。

1.標準命名空間

std是C++標準命名空間,由于C++標準庫幾乎都定義在std命名空間中,因此編寫的所有C++程序都需要引入下列語句。

using namespace std;

2.自定義命名空間

使用nam espace可以自定義命名空間,示例代碼如下所示:

namespace lib 
{ 
     void func(){} 
}

上述代碼中,使用nam espace定義了一個名稱為lib的命名空間,該命名空間內部定義了一個func()函數。

如果要使用命名空間中定義的元素,有下列三種方式。

(1)使用“命名空間::元素”方式指定命名空間中定義的元素。

例如,如果要在屏幕上輸出數據,并且在末尾加上換行,就要使用標準命名空間std中的cout和endl,在使用cout和endl時,在cout和endl前面加上命名空間和“::”作用域標識符,示例代碼如下所示:

std::cout<<"C++"<<std::endl;

(2)使用using語句引用命名空間元素。

例如,如果在屏幕上輸出數據,可以使用using引用標準命名空間中的cout,這樣在后面的代碼中可以隨意使用cout,示例代碼如下所示:

using std::cout;
cout<<"C++";

需要注意的是,這種方式只能使用using引入的元素,例如,無法單獨使用endl這個元素,但可以通過std::endl的形式使用endl。

(3)使用using語句直接引用命名空間。

示例代碼如下所示:

using namespace std;

這樣引入std空間后,std中定義的所有元素就都可以被使用了。但這種情況下,如果引用多個命名空間往往容易出錯。例如,自定義swap()函數,標準庫也有swap()函數,調用swap()函數就會出現二義性錯誤。針對這個問題,可以使用“命名空間::元素”方式指定具體要引用的元素。

多學一招:匿名命名空間

命名空間還可以定義成匿名的,即創建命名空間時不寫名字,由系統自動分配。例如,下面定義的命名空間就是匿名的。

namespace 
{ 
     … //可以是變量、函數、類、其他命名空間 
} 

編譯器在編譯階段會為匿名命名空間生成唯一的名字,這個名字是不可見的。除此之外,編譯器還會為這個匿名命名空間生成一條using指令。編譯后的匿名命名空間等效于下面的代碼:

namespace _UNIQUE_NAME_ 
{ 
    … //可以是變量、函數、類、其他命名空間 
} 
using namespace _UNIQUE_NAME_;

匿名命名空間的作用是限制命名空間的內容僅能被當前源文件使用,其他源文件是無法訪問的,使用extern聲明訪問也是無效的。

1.3.2 控制臺輸入/輸出

C++控制臺常用的輸入/輸出是由輸入、輸出流對象cin和cout實現的,它們定義在頭文件iostream中,作用類似于C語言中的scanf()函數和printf()函數。下面分別對cin與cout的用法進行介紹。

1.cin

cin與運算符“>>”結合使用,用于讀入用戶輸入,以空白(包括空格、Enter、Tab)為分隔符,示例代碼如下所示:

// 輸入單個變量 
char c1,c2; 
cin>>c1; 
cin>>c2; 
// 輸入多個變量 
string s; 
float f; 
cin>>s>>f;

輸入單個變量時,如果從鍵盤輸入字符'a'和'b'(以空格分隔),則cin語句會把輸入的'a'賦值給變量c1,把輸入的'b'賦值給變量c2。

輸入多個變量時,如果輸入字符串"abc"和數據3.14,則cin語句會把字符串"abc"賦值給變量s,把數據3.14賦值給變量f。

2.cout

cout與運算符“<<”結合使用,用于向控制臺輸出信息。cout可以將數據重定向輸出到磁盤文件。具體用法如下。

(1)cout輸出常量值。

示例代碼如下所示:

cout<<10<<endl; 
cout<<'a'<<endl; 
cout<<"C++"<<endl;

(2)cout輸出變量值。示例代碼如下所示:

//輸出單個變量 
int a =10; 
cout<<a<<endl;      //輸出int類型的變量 
//輸出多個變量 
int a = 10; 
char *str = "abc"; 
cout<<a<<","<<str<<endl;

在用cout輸出變量值時,“<<”運算符會根據變量的數據類型自動匹配并正確輸出。

(3)cout輸出指定格式的數據。

使用cout輸出指定格式的數據時,可以通過C++標準庫提供的標志位和操作符控制格式,這些操作符位于iom anip頭文件,使用時需要引入iom anip頭文件。

下面介紹一些常見的輸出格式控制。

①輸出八進制、十進制、十六進制數據。

int a=10; 
cout<<"oct:"<<oct<<a<<endl; // 以八進制輸出a 
cout<<"dec:"<<dec<<a<<endl; // 以十進制輸出a 
cout<<"hex:"<<hex<<a<<endl; // 以十六進制輸出a 

②輸出指定精度數據。

使用setprecision()函數可以設置浮點類型輸出所需精度數據,示例代碼如下所示:

double f = 3.1415926; 
cout<<"默認輸出 :"<<f<<endl; 
cout<<"精度控制"<<setprecision(7) 
 <<setiosflags(ios::fixed)<<f<<endl; 

③輸出指定域寬、對齊方式、填充方式的數據。

C++提供了setw()函數用于指定域寬,setiosflags()函數用于設置對齊方式,setfill()函數用于設置填充方式。

下面通過案例演示如何輸出指定域寬、對齊方式以及填充方式的數據,如例1-2所示。

例1-2 format.cpp

 1  #include<iostream> 
 2  #include<iomanip> 
 3  using namespace std; 
 4  int main() 
 5  { 
 6      cout<<setw(10)<<3.1415<<endl; 
 7      cout<<setw(10)<<setfill('0')<<3.1415<<endl; 
 8      cout<<setw(10)<<setfill('0') 
 9                        <<setiosflags(ios::left)<<3.1415<<endl; 
 10     cout<<setw(10)<<setfill('-') 
 11                       <<setiosflags(ios::right)<<3.1415<<endl; 
 12     return 0; 
 13 } 

例1-2運行結果如圖1-2所示。

圖1-2 例1-2運行結果

在例1-2中,第6行代碼輸出了指定域寬的數據,第7行代碼輸出了填充了字符的數據,第8~11行代碼輸出的是設置了左對齊和右對齊格式的數據。

本節內容只需掌握控制臺輸入/輸出的用法和常見的輸出格式,輸入/輸出標志位和操作符還有很多,其他標志位和操作符可參考附錄I。

1.3.3 類型增強

C語言和C++語言都屬于強類型語言,相比于C語言,C++中的類型檢查更加嚴格,下面介紹C++對常見類型的增強。

1.常變量類型const

使用const修飾的變量稱為常變量,C語言中的常變量可以通過指針修改,而C++中的常變量無法通過指針間接修改。示例代碼如下所示:

const int a = 10;  
int* p = &a;                  // 類型不兼容錯誤 
*p = 20;                      //無法通過指針修改常變量 

2.邏輯類型bool

C語言中沒有邏輯類型,只能用非0表示真,用0表示假。C++增加了bool類型,使用true表示真,false表示假。示例代碼如下所示:

bool a = false;      //定義bool類型變量 
bool b = true; 
bool greater(int x, int y){return x > y;}  // 比較x是否大于y 

3.枚舉類型enum

C語言中枚舉類型只能是整數類型,且枚舉變量可以用任意整數賦值,使用自由靈活。在C++中,枚舉變量只能使用枚舉常量進行賦值。下面代碼在C++中是不被允許的。

enum temperature {WARM,COOL,HOT}; 
enum temperature t= WARM; 
t=10;        //錯誤 

1.3.4 默認參數

“默認”的概念大家都不陌生,比如安裝一款軟件時,在安裝過程中會有默認參數選項,如默認安裝路徑,安裝時可以修改默認安裝路徑。C++的函數支持默認參數,即在聲明或者定義函數時指定參數的默認值。

下面通過案例演示默認參數的用法,如例1-3所示。

例1-3 defaultPara.cpp

 1  #include<iostream> 
 2  using namespace std;  
 3  void add(int x,int y=1,int z=2)  
 4  { 
 5       cout<<x+y+z<<endl; 
 6  } 
 7  int main() 
 8  { 
 9       add(1);       //只傳遞1給形參x,y、z使用默認形參值 
 10      add(1,2);      //傳遞1給x,2給y,z使用默認形參值 
 11      add(1,2,3);  //傳遞三個參數,不使用默認形參值 
 12      return 0; 
 13 }

例1-3運行結果如圖1-3所示。

圖1-3 例1-3運行結果

在例1-3中,第3~6行代碼定義了函數add(),該函數指定了參數y、z的默認值。第9~11行代碼在m ain()中調用了三次add()函數。第一次調用add()函數時,只傳入一個參數1,形參y、z使用默認參數;第二次調用時,傳入參數1、2,形參z使用默認參數;第三次調用時,傳入參數1、2、3,不使用默認參數。由圖1-3可知,三次add()函數均調用成功,都輸出了運算結果。

使用默認參數時,需要注意以下規則。

(1)默認參數只可在函數聲明中出現一次,如果沒有函數聲明,只有函數定義,那么可以在函數定義中設定。

(2)默認參數賦值的順序是自右向左,即如果一個參數設定了默認參數,則其右邊不能存在未賦值的形參。

(3)默認參數調用時,遵循參數調用順序,即有參數傳入時它會先從左向右依次匹配。

(4)默認參數值可以是全局變量、全局常量,甚至可以是一個函數。

1.3.5 函數重載

在平時生活中經常會出現這樣一種情況,一個班里可能同時有兩個甚至多個叫小明的同學,但是他們的身高、體重、外貌等有所不同,老師點名時都會根據他們的特征來區分。在編程語言里也存在這種情況,參數不同的函數有著相同的名字,調用時根據參數不同確定調用哪個函數,這就是C++的函數重載機制。

所謂函數重載(overload),就是在同一個作用域內函數名相同但參數個數或者參數類型不同的函數。例如,在同一個作用域內同時定義三個add()函數,這三個add()函數就是重載函數,示例代碼如下所示:

void add(int x, int y); 
void add(float x); 
double add(double x, double y); 

下面通過案例演示函數重載的用法,如例1-4所示。

例1-4 overloadFunc.cpp

 1  #include <iostream> 
 2  using namespace std; 
 3  void add(int x, int y) 
 4  { 
 5       cout << "int: " << x + y << endl; 
 6  } 
 7  void add(double x) 
 8  { 
 9       cout << "double: " << 10 + x << endl; 
 10 } 
 11 double add(double x, double y) 
 12 { 
 13      return x + y; 
 14 } 
 15 int main() 
 16 { 
 17      add(10.2);    //一個double類型參數 
 18      add(1, 3);    //兩個int類型參數 
 19      return 0; 
 20 }

例1-4運行結果如圖1-4所示。

圖1-4 例1-4運行結果

在例1-4中,第3~14行代碼定義了三個重載函數add()。第17行代碼調用add()函數,傳入一個double類型的實參10.2。第18行代碼調用add()函數,傳入兩個int類型的實參1和3。由圖1-4可知,兩次調用add()函數都成功計算出了結果。

調用重載函數時,編譯器會根據傳入的實參與重載函數逐一匹配,根據匹配結果決定到底調用哪個函數。

如果重載函數中的形參沒有默認參數,定義和調用一般不會出現問題,但是當重載函數有默認參數時,需要注意調用二義性。例如,下面的兩個add()函數:

int add(int x, int y = 1); 
void add(int x);

當調用add()函數時,如果只傳入一個參數就會產生歧義,編譯器無法確認調用哪一個函數,這就產生了調用的二義性。在編寫程序時,要杜絕重載的函數有默認參數,從而避免調用二義性的發生。

1.3.6 引用

引用是C++引入的新語言特性,它是某一變量的一個別名,使用“&”符號標識。引用的定義格式如下:

數據類型& 引用名 = 變量名; 

習慣使用C語言開發的讀者看到“&”符號就會想到取地址。但是在C++引用中,“&”只是起到標識的作用。

下面通過案例演示引用的用法,如例1-5所示。

例1-5 reference.cpp

 1  #include<iostream> 
 2  using namespace std; 
 3  int main() 
 4  { 
 5       int a=10; 
 6       int& ra=a; 
 7       cout<<"變量a的地址"<<hex<<&a<<endl; 
 8       cout<<"引用ra的地址:"<<hex<<&ra<<endl; 
 9       cout<<"引用ra的值:"<<dec<<ra<<endl; 
 10      return 0; 
 11 }

例1-5運行結果如圖1-5所示。

圖1-5 例1-5運行結果

在例1-5中,第5行代碼定義了整型變量a并初始化為10。第6行代碼定義了指向變量a的引用ra。第7~9行代碼分別輸出變量a的地址、引用ra的地址、引用ra的值。由圖1-5可知,引用ra的地址和變量a的地址相同;引用ra的值為10,與變量a的值相同。

在定義引用時,有以下幾點需要注意。

(1)引用在定義時必須初始化,且與變量類型保持一致。

(2)引用在初始化時不能綁定常量值,如int&b=10是錯誤的。

(3)引用在初始化后,其值不能再更改,即不能用作其他變量的引用。

在C++中,引用的一個重要作用是作為函數參數。下面通過案例演示引用作為函數參數的用法,如例1-6所示。

例1-6 quote.cpp

 1  #include<iostream> 
 2  using namespace std; 
 3  void exchange(int& x, int& y) 
 4  { 
 5       int temp = x; 
 6       x = y; 
 7       y = temp; 
 8  } 
 9  int main()
 10 { 
 11      int a, b; 
 12      cout << "please input two nums: " << endl; 
 13      cin >> a >> b; 
 14      exchange(a, b); 
 15      cout << " exchange: " << a << " "<< b << endl; 
 16      return 0; 
 17 } 

例1-6運行結果如圖1-6所示。

在例1-6中,第3~8行代碼定義了一個函數exchange(),用于交換兩個int類型變量的值。exchange()函數有兩個int類型的引用作為參數。第13~15行代碼通過cin從鍵盤輸入兩個整型數據給變量a、b,調用exchange()函數交換變量a、b的值,并輸出交換結果。由圖1-6可知,變量a、b的值交換成功。

圖1-6 例1-6運行結果

在例1-6中,exchange()函數的形參如果為普通變量(值傳遞),由于副本機制無法實現變量a、b的交換。如果形參為指針(址傳遞),可以完成變量a、b的交換,但需要為形參(指針)分配存儲單元,在調用時要反復使用“*指針名”獲取數據,且實參傳遞時要取地址(&a、&b),這樣很容易出現錯誤,且程序的可讀性也會下降。而使用引用作為形參,就克服了值傳遞和址傳遞的缺點,通過引用可以直接操作變量,簡單高效,可讀性又好。

引用是隱式的指針,但引用卻不等同于指針,使用引用與使用指針有著本質的區別。

(1)指針指向一個變量,需要占據額外的內存單元,而引用指向一個變量,不占據額外內存單元。

(2)作為函數參數時,指針的實參是變量的地址,而引用的實參是變量本身,但系統向引用傳遞的是變量的地址而不是變量的值。

顯然,引用比指針更簡單?直觀?方便。使用引用可以代替指針的部分操作。在C語言中只能用指針來處理的問題,在C++中可以通過引用完成,從而降低了程序設計的難度。

如果想使用常量值初始化引用,則引用必須用const修飾,用const修飾的引用稱為const引用,也稱為常引用。

const引用可以用const對象和常量值進行初始化。示例代碼如下所示:

const int &a = 10; 
const int b = 10;    
const int &rb = b;

上述代碼中,第1行代碼定義了const引用a,使用常量10進行初始化;第2行代碼定義常變量b,第3行代碼定義了const引用rb,使用常變量b進行初始化。

常變量的引用必須是const引用,但const引用不是必須使用常量或常變量進行初始化,const引用可以使用普通變量進行初始化,只是使用普通變量初始化const引用時,不允許通過該引用修改變量的值。示例代碼如下所示:

int a = 10;     //變量a 
const int &b = a;    //使用a初始化const引用b 
b=20;     //錯誤

當引用作函數參數時,也可以使用const修飾,表示不能在函數內部修改參數的值。例如下面的函數,比較兩個字符串長度:

bool isLonger(const string &s1, const string &s2) 
{ 
   return s1.size() > s2.size(); 
}

在isLonger()函數中,只能比較兩個字符串長度而不能改變字符串內容。

1.3.7 字符串類

C語言不存在字符串類型,都是用字符數組處理字符串,C++支持C風格的字符串,另外還提供了一種字符串數據類型:string。string是定義在頭文件string中的類,使用前需要包含頭文件string。

使用string定義字符串比較簡單,主要有以下幾種方式:

string s1; 
s1="hello C++";    //第一種方式 
string s2="hello C++";    //第二種方式 
string s3("hello C++");    //第三種方式 
string s4(6,'a');    //第四種方式 

第一種方式先定義了字符串變量s1,再為字符串變量s1賦值;第二種方式直接使用“=”為字符串變量s2賦值;第三種方式在定義字符串變量時,將初始值放在“()”運算符中,使用“()”運算符中的字符串為變量初始化;第四種方式在定義字符串變量時,也將初始值放在“()”運算符中,但是“()”中有兩個參數,第一個參數表示字符個數,第二個參數表示構成字符串的字符。上述代碼最后一行,表示用6個字符'a'構成的字符串初始化變量s4,初始化后s4的值為"aaaaaa"。

注意:

使用string定義字符串時,不需要擔心字符串長度、內存不足等情況,而且string類重載的運算符與成員函數足以完成字符串的各種處理操作。

下面介紹一些常見的string字符串操作。

1.訪問字符串中的字符

string類重載了“[]”運算符,可以通過索引方式訪問和操作字符串中指定位置的字符。示例代碼如下所示:

string s="hello,C++"; 
s[7]='P'; 
s[8]='P';

上述代碼中,通過索引將字符串s中的兩個“+”都修改成了'P'。

2.字符串的連接

在C語言中,連接兩個字符串要調用strcat()函數,還要考慮內存溢出情況。在C++中,string重載了“+”運算符,可以使用“+”運算符連接兩個string類型的字符串,示例代碼如下所示:

string s1,s2; 
s1="我在學習"; 
s2="C++"; 
cout<<s1+s2<<endl;     //我在學習C++ 

3.字符串的比較

在C語言中,比較兩個字符串是否相等需要調用strcm p()函數,而在C++中,可以直接調用重載的“>”“<”“==”等運算符比較兩個string字符串。示例代碼如下所示:

string s1,s2; 
cin>>s1>>s2; 
//比較兩個字符串內容是否相同 
if(s1>s2) 
    cout<<"字符串s1大于s2"<<endl;  
else if (s1<s2)  
    cout<<"字符串s2大于s1"<<endl; 
else 
    cout<<"字符串s1與s2相等"<<endl; 

上述代碼通過“>”“<”“==”運算符比較用戶輸入的兩個字符串的內容是否相同。

4.字符串的長度計算

string類提供的length()函數用于獲取字符串長度。length()函數類似于C語言中的strlen()函數。調用length()函數獲取字符串長度的示例代碼如下所示:

string s="hello C++";
cout<<"length():"<<s.length()<<endl;

需要注意的是,由于計算結果不包括字符串末尾結束標志符“\0”,因此,上述代碼使用length()函數計算出字符串s的長度為9。

5.字符串交換

string類提供了成員函數swap(),用于交換兩個字符串的值,示例代碼如下所示:

string s1="hello C++"; 
string s2="I Love China!"; 
s1.swap(s2);     //通過“.”運算符方式交換
swap(s1,s2);     //通過函數調用方式交換 

需要注意的是,string的成員函數swap()只能交換string類型的字符串,不能交換C語言風格的字符串。

1.3.8 new/delete

C++增加了new運算符分配堆內存,delete運算符釋放堆內存。具體用法如下。

1.使用new運算符分配堆內存

new運算符用于申請一塊連續的內存,格式如下:

new 數據類型(初始化列表);

上述格式中,數據類型表示申請的內存空間要存儲數據的類型;初始化列表指的是要存儲的數據。如果暫時不存儲數據,初始化列表可以為空,或者數據類型后面沒有()。如果內存申請成功,則new返回一個具體類型的指針;如果內存申請失敗,則new返回NULL。

new申請內存空間的過程,通常稱為new一個對象。與m alloc()相比,new創建動態對象時不必為對象命名,直接指定數據類型即可,并且new能夠根據初始化列表中的值進行初始化。

下面介紹new運算符常見的幾種用法。

(1)創建基本數據類型對象。

使用new創建基本數據類型對象,示例代碼如下所示:

char* pc = new char;         //存儲char類型的數據 
int* pi = new int(10);       //存儲int類型的數據 
double* pd = new double();   //存儲double類型的數據

上述代碼分別用new創建了char、int、double三個對象。其中,char對象沒有初始化列表,新分配內存中沒有初始值;int對象初始化列表為10,即分配一塊內存空間,并把10存入該空間;double對象初始化列表為空,編譯器會用0初始化該對象。

(2)創建數組類型對象。

使用new創建數組對象,格式如下所示:

new 數據類型[數組長度];

使用new創建數組的示例代碼如下所示:

char* pc = new char[10];

在上述代碼中,指針pc指向大小為10的char類型數組。

2.使用delete運算符釋放堆內存

用new運算符分配的內存在使用后要及時釋放以免造成內存泄漏,C++提供了delete運算符釋放new出來的內存空間,格式如下:

delete 指針名;

由上述格式可知,delete運算符直接作用于指針就可以釋放指針所指向的內存空間。但是使用delete運算符釋放數組對象時要在指針名前加上[],格式如下:

delete[]指針名;

如果漏掉了[],編譯器在編譯時無法發現錯誤,導致內存泄漏。下面通過案例來演示new和delete的用法,如例1-7所示。

例1-7 allocMeorry.cpp

 1  #include<iostream> 
 2  using namespace std
 3  int main() 
 4  { 
 5       int* pi = new int(10);    //創建一個int對象,初始值為10 
 6       cout<<"*pi="<<*pi<<endl; 
 7       *pi = 20;         //通過指針改變內存中的值 
 8       cout<<"*pi = "<<*pi<<endl; 
 9       //創建一個大小為10的char類型的數組 
 10      char* pc = new char[10]; 
 11      for(int i = 0;i < 10;i++) 
 12      pc[i] = i + 65;       //向數組中存入元素 
 13      for(int i = 0;i < 10;i++) 
 14      cout<<pc[i]<<" "; 
 15      cout<<endl; 
 16      delete pi;          //釋放int對象 
 17      delete []pc;       //釋放char數組對象 
 18      return 0; 
 19 }

例1-7運行結果如圖1-7所示。

在例1-7中,第5行代碼使用new創建了一個int對象,初始值為10。第6行代碼通過指針pi輸出內存中的數據,由圖1-7可知,輸出結果為10。第7~8行代碼通過指針pi修改內存中的數據為20,并輸出,由圖1-7可知,輸出結果為20。第10~12行代碼使用new創建一個大小為10的char類型數組,并通過for循環為數組賦值。第13~14行代碼通過for循環輸出數組中的元素,由圖1-7可知,數組中的元素成功輸出。第16~17行代碼使用delete運算符釋放int對象和char類型數組對象。

圖1-7 例1-7運行結果

1.3.9 extern"C"

在C++程序中,可以使用extern"C"標注C語言代碼,編譯器會將extern"C"標注的代碼以C語言的方式編譯。使用extern"C"標注C語言代碼的格式具體如下:

extern"C" 
{ 
  // C語言代碼 
}

下面通過案例演示在C++程序中編譯C語言程序,這個案例包括m allocStr.h、m allocStr.c和m ain.cpp三個文件,三個文件的實現分別如例1-8~例1-10所示。

例1-8 mallocStr.h

 1  #include<stdio.h> 
 2  #include<stdlib.h> 
 3  char* func(int,char*

例1-9 mallocStr.c

 1  #define _CRT_SECURE_NO_WARNINGS 
 2  #include"mallocStr.h" 
 3  char* func(int size,char *str) 
 4  { 
 5       char* p =malloc(size); 
 6       strcpy(p,str); 
 7       return p; 
 8  }

例1-10 main.cpp

  1 #include<iostream>
  2 using namespace std;
  3 #ifdef __cplusplus
  4 extern"C"
  5 {
  6 #endif
  7 #include"mallocStr.h"
  8 #ifdef __cplusplus
  9 }
  10 #endif
  11 int main()
  12 {
  13 char str[]="C++";
  14 char *p=func(sizeof(str)+1,str);
  15 cout<<p<<endl;
  16 free (p);
  17 return 0;
  18 }

例1-10運行結果如圖1-8所示。

圖1-8 例1-10運行結果

例1-8和例1-9的m allocStr.h文件和m allocStr.c文件所示代碼是C語言程序。其中,m allocStr.c文件中定義了func()函數,在函數內部調用m alloc()函數申請一塊內存空間存儲一個字符串。func()函數第一個參數指定申請內存的大小,第二個參數是存入內存空間的字符串。

在例1-10所示的m ain.cpp中,程序調用了func()函數,則需要使用extern"C"聲明m allocStr.h文件內容以C語言的方式編譯。

1.3.10 強制類型轉換

與C語言的類型轉換相比,C++的類型轉換機制更加安全。C++提供了四個類型轉換運算符應對不同類型數據之間的轉換,下面分別進行介紹。

1.static_cast<type>(expression)

static_cast<>是最常用的類型轉換運算符,主要執行非多態的轉換,用于代替C語言中通常的轉換操作。static_cast<>可以實現下列轉換。

  • 基本數據類型之間的轉換。
  • 將任何類型轉換為void類型。
  • 把空指針轉換成目標類型的指針。
  • 用于類層次結構中基類和派生類之間指針或引用的轉換。向上轉換(派生類轉換為基類)是安全的;向下轉換(基類轉換為派生類)沒有動態類型檢查,是不安全的。

使用static_cast<>運算符進行類型轉換的示例代碼如下所示:

int a=1; 
float b=3.14; 
a=static_cast<int>(b);    //將float類型轉換為int類型 
b=static_cast<float>(a);  //將int類型轉換為float類型 
int *q=NULL; 
void* p = NULL; 
q=p;      //將空指針轉換為int類型,C語言允許,C++不允許 
p=q; 
q=static_cast<int*>(p);   //將空指針轉換為int類型指針 

2.reinterpret_cast<type>(expression)

reinterpret_cast通常為操作數的位模式提供較低層的重新解釋。例如,如果將一個int類型的數據a轉換為double類型的數據b,僅僅是將a的比特位復制給b,不作數據轉換,也不進行類型檢查。reinterpret_cast要轉換的類型必須是指針類型、引用或算術類型。

使用reinterpret_cast<>運算符進行類型轉換的示例代碼具體如下:

char c = 'a'; 
int d = reinterpret_cast<int&>(c); 
int *p=NULL;  
float *q=NULL; 
p = q;                                //C 語言允許,C++語言不允許 
q = p;                                //C 語言允許,C++語言不允許 
p = static_cast<int*>(q);         //static_cast無法轉換 
q = static_cast<int*>(p);         //static_cast無法轉換 
p = reinterpret_cast<int*>(q); 
q = reinterpret_cast<float*>(p)

3.const_cast<type>(expression)

const_cast<>用于移除const對象的引用或指針具有的常量性質,可以去除const對引用和指針的限定。示例代碼如下所示:

int num = 100; 
const int* p1 = &num; 
//將常量指針轉換為普通類型指針,去除const屬性 
 int* p2 = const_cast<int*>(p1); 
*p2 = 200; 
int a=100; 
const int & ra=a; 
//將常量引用轉換為普通類型引用,去除const屬性 
const_cast<int&>(ra)=200; 

需要注意的是,const_cast<>只能用于轉換指針或引用。

4.dynamic_cast<type>(expression)

dynam ic_cast<>用于運行時檢查類型轉換是否安全,可以在程序運行期間確定數據類型,如類的指針、類的引用和void*。dynam ic_cast<>主要應用于類層次間的向上轉換和向下轉換,以及類之間的交叉轉換。在類層次間進行向上轉換時,它和static_cast作用一致。不過,與static_cast相比,dynam ic_cast能夠在運行時檢查類型轉換是否安全。

當向下轉換時,如果基類指針或引用指向派生類對象,dynam ic_cast運算符會返回轉換后類型的指針,這樣的轉換是安全的。如果基類指針或引用沒有指向派生類對象,則轉換是不安全的,轉換失敗時就返回NULL。

多學一招:Bjarne Stroustrup對編寫C++程序的建議

1.C++中幾乎不需要用宏。

2.用const或enum定義顯式的常量。

3.用inline避免函數調用的額外開銷。

4.用模板定義函數或類型。

5.用namespace避免命名沖突。

6.變量在使用時聲明并初始化,不要提前聲明變量。

7.使用new和delete會比函數malloc()和free()更好,realloc()函數可以用vector()代替。

8.避免使用void*、指針算術、聯合和強制轉換。

9.盡量少用數組和C風格的字符串,標準庫中的string和vector可以簡化程序。

10.試著將程序考慮為一組由類和對象表示的相互作用的概念。

主站蜘蛛池模板: 客服| 兴隆县| 白水县| 聂拉木县| 西峡县| 兴和县| 齐齐哈尔市| 南漳县| 潜江市| 凭祥市| 新巴尔虎右旗| 花垣县| 永寿县| 汉中市| 弋阳县| 万山特区| 萍乡市| 云林县| 龙南县| 大悟县| 盐池县| 喀喇沁旗| 射洪县| 云阳县| 包头市| 深州市| 社旗县| 汤阴县| 颍上县| 宁武县| 米林县| 翼城县| 朝阳市| 普宁市| 准格尔旗| 庆元县| 常宁市| 平和县| 吴桥县| 开化县| 睢宁县|