c++學習筆記(4.c++中新的關鍵字)

本節知識點:

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更安全。這個問題等對類有了理解再回來補充。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章