C++對C的擴展
1. 雙冒號::作用域運算符
代碼中對同一個變量多次聲明,在代碼塊中使用時,局部變量會將全局變量隱藏。若在代碼塊使用變量前添加::,表示為全局變量。
::表示作用域運算符,如常見的std::cout,std::endl;等,表示cout和endl是std作用域下的標識符。
2. 命名空間namespace
主要用來解決命名沖突的問題,如多個人開發的不同模塊中使用了相同的變量名和函數名,fatal error LNK1169:找到一個或多個重定義的符號,這時可以使用命名空間,將不同的模塊分隔開。
1 namespace QGY{ 2 int a; 3 void test(); 4 struct QGYTEST{ 5 int b; 6 }; 7 class QGYNUM{}; 8 }
使用命名空間的注意事項:(1)必須在全局作用域下聲明;(2)命名空間下可以放函數,變量、結構體和類;(3)命名空間可以嵌套命名空間;(4)命名空間是開放的,可以隨時加入新成員(添加時只需要再次聲明namespace,然后添加新成員即可,示例如下);(5)無名或匿名命名空間,相當於static變量;(6)可以對命名空間起別名(一般不用)
namespace QGY{ int m ; }
3. using聲明和using編譯指令
using QGY::a; //聲明 using namespace QGY; //編譯指令
對於聲明來說,如果局部范圍內還有a,會出現二義性,程序不知道使用哪一個,因此應避免這種情況.
1 void test01(){ 2 int a = 10; 3 using QGY::a; //這里在聲明的時候不能進行賦值,可以在下一行,a = 20; 4 std::cout << a << std::endl; 5 }
這里程序會出現錯誤, error C2874: using 聲明導致“QGY::a”的多次聲明。
對於編譯指令,如果局部范圍還有a,會使用局部變量。如果還有另外的命名空間也聲明了a,且同時打開了其他空間,則也會出現二義性。
1 void test02(){ 2 int a = 10; 3 using namespace QGY; //這里只是打開空間,並沒有指定使用 4 std::cout << a << std::endl; 5 }
1 void test03(){ 2 using namespace QGY; //只是打開房間就可以訪問到a,打開多個房間就會產生二義性 3 std::cout << a << std::endl; 4 }
C++對C的增強
1.全局變量檢測增強
C語言會忽略對全局變量重定義的檢測,但不會忽略對局部變量的檢測,C++中都會報錯:error C2086: “int a”: 重定義
//全局變量不會報錯 int a; int a = 10; //局部變量會報錯 void test(){ int a; int a = 10; }
2.函數檢測增強:包括函數形參類型檢測,形參數目檢測,函數返回值檢測,C都會忽略,C++不會
1 //C中函數形參沒有參數類型,沒有返回值,調用參數過多都會忽略 2 int test(m, n){ 3 4 } 5 6 void test01(){ 7 test(1,2,3); 8 }
3.類型轉換檢測增強
C語言中malloc開辟內存空間時默認生成void*指針,可以轉換成任意指針,C++中則不行,必須顯式的進行強制轉換。
4.struct增強
(1)C中strcut中不能有函數,C++中可以有,並且與class的區別在於是否有私有成員,和是否有構造函數;
(2)通過如下方式聲明struct時,C語言定義使用結構體時必須使用struct,C++可以不用。
1 struct Person{ 2 int a; 3 }; 4 5 struct Person myperson; //C 6 Person myperson; //C++
5.bool類型增強
C語言中沒有bool類型,C++中有bool類型,其中sizeof(bool)=1
6.三目運算符增強
1 a > b?a : b;
C語言中返回的是值,C++中返回的是變量,C語言中下面代碼會報錯:error C2106: “=”: 左操作數必須為左值,表明代碼中為20=100,所以會報錯。
1 void test01(){ 2 int a = 10; 3 int b = 20; 4 printf("%d\n", a > b? a:b); 5 a > b ? a : b = 100; 6 }
如果想改變三目運算符后的結果,可以按照如下代碼進行修改
1 *(a > b? &a:&b) = 100;
C++則不會,因為C++三目運算后為變量,因此可以進行賦值操作,其中a = 10, b = 100;
1 void test01(){ 2 int a = 10; 3 int b = 20; 4 a > b ? a : b = 100; 5 cout << a << " " << b << endl; 6 }
另外下面三種情況下的a和b的值是不同的
1 //a=100, b=20 2 (a < b ? a : b) = 100; 3 4 //a=10, b=100 5 (a > b ? a : b) = 100; 6 7 //a=10, b=20 8 a < b ? a : b = 100;
最后一種情況,不會執行b=100,其中帶括號的是按照我們的想法去執行代碼,不帶括號的話,優先級不同導致結果和預想的不同。
7.const增強
(1)是否可以修改
C語言中的全局const不可修改,是真常量,如果對其修改會出現訪問沖突,另外不可以聲明數組的大小(這是C的缺陷,也是為什么替代不了define);局部const為偽常量,可以進行修改,同時不可以用於聲明數組的大小(真假都不可以聲明數組的大小)。
1 const int m = 0; //全局靜態變量受到保護,不可修改 2 void test01(){ 3 const int n = 1; //偽常量,可以通過地址進行修改 4 int *p = &n; //可以不加強制轉換 5 *p = 100; 6 printf("%d\n", n); 7 8 int am[n]; //n不可用於聲明大小,不是常量值 9 }
C++不管全局還是局部都是真常量,不可修改,同時可以初始化數組,原因如下(取地址時會分配臨時內存):
(2)鏈接屬性
C語言的const默認是外部鏈接,C++默認是內部鏈接
1 //1.cpp 2 const int a = 10; 3 4 //2.cpp 5 extern const int a;
C語言中進行訪問時可以的,但C++中需要在1.cpp的聲明前加extern否則無法使用
(3)const分配內存
是否分配內存,我們可以根據const修飾的變量是否能夠修改來確定。
編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內存的操作,使得它的效率也很高,但是在下列幾種情況下編譯器會為const定義的常量分配內存的。
(3.1)取地址時,const會分配臨時內存,不可以進行間接修改
1 void test003(){ 2 const int m = 10; 3 int *p = (int *)&m;//分配臨時內存,不可以進行間接修改 4 *p = 100; 5 cout << m << endl; 6 }
(3.2)extern編譯器也會為變量分配內存
因為使用了extern,我們將可能在外部文件使用該變量,而const默認的是內部鏈接,所以我們必須要為之分配內存的。
(3.3)用普通變量初始化const變量,會分配內存
1 void test003(){ 2 int m = 10; 3 const int b = (int *)&m; //分配內存,且可以跳過編譯器檢測進行間接修改,另外需要加強制轉換 4 int *p = &b; 5 *p = 100; 6 }
(3.4)自定義數據,加const也會分配內存
1 struct Person{ 2 int age; 3 }; 4 5 void test004(){ 6 const Person person = {10};//分配內存,且可以跳過編譯器檢測進行間接修改 7 8 Person *b = (Person*)&person; 9 (*b).age = 20; 10 cout << (*b).age << endl; 11 }
(4)盡量用const替換define
對於常量,盡量替換因為const有數據類型檢查和作用域。另外define用於宏定義時,可以用undef進行解除。但是對於有些條件編譯時需要define,則不能替換。
define是應用於預處理的,而const是在編譯的時候處理的。對於單純常量,使用const與enum代替宏,對於函數形式的宏,則使用inline與template替代。