本節知識點:
1.關鍵字new與delete:
a.c++中通過new關鍵字進行動態內存分配,new是一種基於類型進行的內存分配,同時c++使用delete進行內存釋放
單個變量內存申請與釋放:Type* p = new Type; delete p;
一段內存空間的申請與釋放:Type* p = new Type [N]; delete [] p;
示例代碼如下:
#include <stdio.h> #include <malloc.h> int main() { /*使用new申請單個變量*/ double* p = new double; *p = 7.2; printf("%f\n",*p); delete p; int *p1 = (int*)malloc(sizeof(int)*10); for(int i = 0; i < 10; i++) { *(p1+i) = i; } for(int i = 0; i < 10; i++) { printf("%d\n",p1[i]); } free(p1); /*使用new申請一段連續的內存空間*/ //new出來的這個空間看着像數組 但它跟數組有本質區別 這是在堆區的 int *p2 = new int[10]; for(int i = 10; i > 0; i--) { p2[10-i] = i; } for(int i = 0; i < 10; i++) { printf("%d\n",*(p2+i)); } delete [] p2; return 0; }
b.new關鍵字與malloc函數的區別:
第一:new關鍵字是c++的一部分,而malloc是由c庫提供的函數
第二:new是以具體類型爲單位進行內存分配,malloc只能以字節爲單位進行內存分配
第三:new在申請單個類型變量時可進行初始化,malloc不具備內存初始化的特性,代碼如下:
#include <stdio.h> int main() { int* p = new int(6); printf("%d\n",*p); double* fp = new double(7.2); printf("%f\n",*fp); char* cp = new char('d'); printf("%c\n",*cp); return 0; }
2.c++中的命名空間:
a.命名空間產生的原因:在c語言中只有一個全局作用區,所有的全局標識符(包括全局變量、函數)共享同一個全局作用區。儘管有static關鍵字的限制,但是在工程中依然存在接口函數名之間,或與庫函數名衝突的現象。所以c++中提出了命名空間的概念,命名空間將全局作用區分成了若干的命名空間和無名空間(即默認空間,也就是兼容c語言的全局作用區的空間)。
b.命名空間的有效作用域:在c語言中沒有命名空間的概念,導致所有在全局作用區的全局標識符的作用域都是,從這個變量或者函數定義聲明開始一直有效到程序結束(從始至終貫穿着整個程序,所有很容易就衝突了)。而c++中有了命名空間的概念後,一旦在一個函數中使用using namespace name1; using name1::a;這樣的語句的時候,name1這個命名空間的作用域和name1中a變量的作用域,就是從using命令開始到這個函數結束(也就是說可以通過命名空間來控制全局變量的作用域了)。如果是在一個命名空間name2中使用using命令的話,那name1和a的作用域就屬於name2了(即與name2屬於相同作用域)。如果在默認命名空間(無名空間)中使用using命令,name1和a的作用域就變化與c語言一樣的那個全局作用區了。如果使用name1::a這樣方法直接去控制變量的話,那作用域就是使用瞬間。
c.命名空間的語法:
定義命名空間(要在函數外面定義):
namespace name2 //這個是命名空間的嵌套 { int c = 2; int f = 5; namespace name3 { int d = 3; int e = 4; } }
使用命名空間(有三種方式):
第一:using spacename name1; 開啓name1這個命名空間,可以使用這個命名空間中的所有變量了。
第二:using name1::a; 可以使用name1命名空間中的a變量了。
第三:name1::a = 10; 直接使用name1命名空間的a變量
第四:::a = 10; 或者 a = 10; 直接使用默認命名空間中的變量
d.使用命名空間要注意:
第一:命名空間是可以相互嵌套的,且有一個嵌套順序的問題,不同嵌套順序的命名空間,即使名稱相同,也是不同的命名空間。因爲using的時候,是有順序的。
第二:相同名稱的命名空間,在using的時候,兩個命名空間都會被打開。只要相同名稱的命名空間中沒有同名變量就可以!否則在使用這個變量的時候,就會出現二義性,編譯會出問題。
第三:不同命名空間中,標識符可以同名而不會發生衝突!因爲在使用的時候,不一定會同時using這兩個命名空間,所以不一定會產生二義性!但是如果同時打開這兩個命名空間,還去使用了這個同名變量,編譯也會出錯,因爲產生二義性!但是當using spacename name1;(name1中有變量a) 並且using name2::a的時候,再使用a變量的時候就沒有二義性了,編譯器默認選擇name2中的a變量使用。代碼如下:
#include<stdio.h> namespace name1 { int a = 9; namespace name1 { int a = 10; } } namespace name2 { int p = 19; int a = 0; } int main() { using namespace name1; using name2::a; printf("%d\n",a); return 0; }
3.c++中的強制類型轉換:
a.c語言中的強制類型轉換:有兩種方式,(Type) name 和 Type (name) 注意第二種方式不是很常用。
b.c語言中強制類型轉換存在的問題:
第一:過於粗暴,任何類型之間都可以進行轉換,編譯器很難判斷其正確性。
第二:難於定位,在源碼中無法快速定位所有使用強制類型轉換的語句。
補充:在程序設計理論中強制類型轉換不被推薦,與goto語句一樣,應該進行避免,因爲在程序設計過程中,定義變量就應該清楚變量的使用環境,直接定義成有效的類型,避免強制類型轉換。還有就是現代的軟件產品裏面有三個問題是bug的源泉:有運算符的優先級問題、多線程編程中各個線程的交互、強制類型轉換。
c.c++中將強制類型轉換分成四個類型:
static_cast類型:用於基本類型間的轉換,但不能用於指針類型間的轉換。用於有繼承關係對象之間的轉換和類指針之間的轉換。但是static_cast是編譯期進行的轉換,無法在運行時檢測類型,所以類類型之間的轉換可能存在風險。代碼如下:
#include <stdio.h> int main() { int a = 99; double b = static_cast<double>(a); printf("%f\n",b); printf("%c\n",static_cast<char>(b)); getchar(); return 0; }
const_cast類型:用於去除變量的const屬性,注意,它只能去除const int* p或者const int& p這兩種類型中的const屬性。比如int* const p類型中的const屬性是去不掉的,其實不管用什麼手段這個const的屬性都是去不掉的,int* const p = &a; p一旦指向就不能再改變指向,這其實就是引用的實質!!!
const_cast類型有三個常用環境:第一,去除const int &j;的const只讀屬性、第二,去除const int * j;的const只讀屬性、第三,去除const int j;的const只讀屬性。代碼如下:
#include <stdio.h> int main() { /*const int &j 去除const屬性*/ const int& j = 4; const_cast<int&>(j) = 6; //其實實際上j就是一個只讀變量 利用指針也可以改變j的值的 // *(int*)(&j) = 6; //這步的強制類型轉換與上面那句代碼等效 printf("j is %d\n",j); int& i = const_cast<int&>(j); i = 12; printf("j is %d\n",i); /*const int* p 去除const屬性*/ int a = 1; const int* p = &a; *const_cast<int*>(p) = 2; // *(int* )p = 2; //與上面代碼等效的強制類型轉換 printf("*p is %d\n",*p); /*const int l = 6*/ const int l = 16; const_cast<int&>(l) = 17; // (int&)l = 17; //與上面語句等效的強制類型轉換 // *const_cast<int*>(&l) = 17; // *(int*)&l = 17; printf("l is %d\n",l); return 0; }
對於上面代碼有兩點要說明:
第一:const_cast其實只讀const指針和const引用起作用了,對const常量沒有起作用,因爲const常量是保存在符號表中的。
第二:對於引用的強制類型轉換,int a = 10; (char)a這是將a變量中的值強制類型轉換成char類型,切記此時(char)a不是變量不能當做左值使用。但是(char&)a是將a強制類型轉換成char類型的引用,其實是一個變量可以當做左值使用!對於const_cast<int&>(l) = 17; 來說l常量是存放在符號表中的,而const_cast<int&>(l)其實是個有內存空間的變量,其實相當於對l取地址的時候,編譯器分配了一個內存空間,這個空間不是用來存放l常量的,而是強制類型轉換後這個引用的內存空間,所以說這個17就存放在這個引用的內存空間了!!!
reinterpret_cast類型:用於指針類型間的強制類型轉換,用於整數和指針間的強制類型轉換,還用於引用間的強制類型轉換。(注:此處指針引用的強制類型轉換都是沒有const屬性去除過程的,否則優先使用const_cast)
引用間的強制類型轉換(代碼如下):
#include <stdio.h> int main() { int a = 10; int &b = a; char &c = reinterpret_cast<char&>(b); printf("%d\n",reinterpret_cast<char&>(b)); a = 129; printf("a is %d\n",a); printf("b is %d\n",b); printf("c is %d\n",c); return 0; }
我們仔細說說代碼中c引用與b引用的關係,不可否定的是abc三個變量,有其中任何一個變量改變了,都會對另外的兩個起到改變的作用!a與b僅僅是名字不同的問題(其餘完全相同),但是c引用是與b引用變成char類型引用變量後完全相同的。
reinterpret_cast類型用法,代碼如下:
#include <stdio.h> int main() { int a = 10; *reinterpret_cast<unsigned int*>(&a) = 12; printf("a is %d\n",a); int* b = &a; char* c = reinterpret_cast<char*>(b); *c = 14; printf("a is %d\n",*c); return 0; }
reinterpret_cast是直接從二進制位進行復制,是一種極其不安全的轉換。
dynamic_cast類型:主要用於類層次間的轉換,還可以用於類之間的交叉轉換。dymanic_cast具有類型檢查的功能,比static_cast更安全。這個問題等對類有了理解再回來補充。