1. 程式人生 > >C++ 學習基礎篇(一)—— C++與C 的區別

C++ 學習基礎篇(一)—— C++與C 的區別

      程式設計的學習學無止境,只掌握一門語言是遠遠不夠的,現在我們開始C++的學習之路,下面先看下C++ 與C 的區別

一、C++概述

1、發展歷史

      1980年,Bjarne Stroustrup博士開始著手建立一種模擬語言,能夠具有面向物件的程式設計特色。在當時,面向物件程式設計還是一個比較新的理念,Stroustrup博士並不是從頭開始設計新語言,而是在C語言的基礎上進行建立。這就是C++語言。

     1985年,C++開始在外面慢慢流行。經過多年的發展,C++已經有了多個版本。為次,ANSI和ISO的聯合委員會於1989年著手為C++制定標準。1994年2月,該委員會出版了第一份非正式草案,1998年正式推出了C++的國際標準。

2、C和C++

      C++是C的超集,也可以說C是C++的子集,因為C先出現。按常理說,C++編譯器能夠編譯任何C程式,但是C和C++還是有一些小差別。

      例如C++增加了C不具有的關鍵字。這些關鍵字能作為函式和變數的識別符號在C程式中使用,儘管C++包含了所有的C,但顯然沒有任何C++編譯器能編譯這樣的C程式。

     C程式設計師可以省略函式原型,而C++不可以,一個不帶引數的C函式原型必須把void寫出來。而C++可以使用空引數列表

     C++中new和delete是對記憶體分配的運算子,取代了C中的mallocfree

     標準C++中的字串類取代了C標準C函式庫標頭檔案中的字元陣列處理函式(C中沒有字串型別)。

     C++中用來做控制態輸入輸出的iostream類庫替代了標準C中的stdio函式庫

     C++中的try/catch/throw異常處理機制取代了標準C中的setjmp()和longjmp()函式。

二、關鍵字和變數

      C++相對與C增加了一些關鍵字,如下:

typename bool dynamic_cast mutable namespace

static_cast using catch explicit new

virtual operator false private template

volatile const protected this wchar_t

const_cast public throw friend true

reinterpret_cast try

bitor xor_e and_eq compl or_eq

not_eq bitand

在C++中還增加了bool型變數wchar_t型變數

布林型變數是有兩種邏輯狀態的變數,它包含兩個值:真和假。如果在表示式中使用了布林型變數,那麼將根據變數值的真假而賦予整型值1或0。要把一個整型變數轉換成布林型變數,如果整型值為0,則其布林型值為假;反之如果整型值為非0,則其布林型值為真。布兒型變數在執行時通常用做標誌,比如進行邏輯測試以改變程式流程。

#include iostream.h
       
int main()
{           
	bool flag;
	flag = true;
	if(flag)
		cout << true << endl;

	return 0;
}

C++中還包括wchar_t資料型別,wchar_t也是字元型別,但是是那些寬度超過8位的資料型別。許多外文字符集所含的數目超過256個,char字元型別無法完全囊括。wchar_t資料型別一般為16位。

標準C++的iostream類庫中包括了可以支援寬字元的類和物件。用wout替代cout即可。

#include iostream.h

int main()
{
	wchar_t wc;
	wc = 'b';
	wout << wc;
	wc = 'y';
	wout << wc;
	wc = 'e';
	wout << wc << endl;

	return 0;
}

說明一下:某些編譯器無法編譯該程式(不支援該資料型別)。

三、強制型別轉換

     有時候,根據表示式的需要,某個資料需要被當成另外的資料型別來處理,這時,就需要強制編譯器把變數或常數由宣告時的型別轉換成需要的型別。為此,就要使用強制型別轉換說明,格式如下:

int* iptr=(int*) &table;

表示式的字首(int*)就是傳統C風格的強制型別轉換說明(typecast),又可稱為強制轉換說明(cast)。強制轉換說明告訴編譯器把表示式轉換成指定的型別。有些情況下強制轉換是禁用的,例如不能把一個結構型別轉換成其他任何型別。數字型別和數字型別、指標和指標之間可以相互轉換。當然,數字型別和指標型別也可以相互轉換,但通常認為這樣做是不安全而且也是沒必要的。強制型別轉換可以避免編譯器的警告。

long int el = 123;
short i = (int) el;

float m = 34.56;
int i = (int) m;

上面兩個都是C風格的強制型別轉換,C++還增加了一種轉換方式,比較一下上面和下面這個書寫方式的不同:

long int el = 123;
short i = int (el);


float m = 34.56;
int i = int (m); 

使用強制型別轉換的最大好處就是:禁止編譯器對你故意去做的事發出警告。但是,利用強制型別轉換說明使得編譯器的型別檢查機制失效,這不是明智的選擇。通常,是不提倡進行強制型別轉換的。除非不可避免,如要呼叫malloc()函式時要用的void型指標轉換成指定型別指標。

四、標準輸入輸出流

在C語言中,輸入輸出是使用語句scanf()printf()來實現的,而C++中是使用類來實現的

#include iostream.h

main()    //C++中main()函式預設為int型,而C語言中預設為void型。
{
	int a;
	cout << input a number: ;
	cin >> a;             /*輸入一個數值*/
	cout << a << endl;      //輸出並回車換行
	
	return 0;
}

    cin,cout,endl物件,他們本身並不是C++語言的組成部分。雖然他們已經是ANSI標準C++中被定義,但是他們不是語言的內在組成部分。在C++中不提供內在的輸入輸出運算子,這與其他語言是不同的。輸入和輸出是通過C++類來實現的,cin和cout是這些類的例項,他們是在C++語言的外部實現。

     在C++語言中,有了一種新的註釋方法,就是‘//’,在該行//後的所有說明都被編譯器認為是註釋,這種註釋不能換行。C++中仍然保留了傳統C語言的註釋風格/*……*/。
C++也可採用格式化輸出的方法:
#include iostream.h
       
int main()
{          
	int a;           
	cout << input a number: ;         
	cin >> a;
           
	cout << dec << a << ' '     //輸出十進位制數               
	<< oct << a << ' '     //輸出八進位制數               
	<< hex << a << endl;   //輸出十六進位制數
           
	return 0;       
}

從上面也可以看出,dec,oct,hex也不可作為變數的識別符號在程式中出現。

五、函式引數問題

1、無名的函式形參

       宣告函式時可以包含一個或多個用不到的形式引數。這種情況多出現在用一個通用的函式指標呼叫多個函式的場合,其中有些函式不需要函式指標宣告中的所有引數。看下面的例子:
int fun(int x,int y)
{
	return x*2;
}
    儘管這樣的用法是正確的,但大多數C和C++的編譯器都會給出一個警告,說引數y在程式中沒有被用到。為了避免這樣的警告,C++允許宣告一個無名形參,以告訴編譯器存在該引數,且呼叫者需要為其傳遞一個實際引數,但是函式不會用到這個引數。下面給出使用了無名引數的C++函式程式碼:
int fun(int x,int) //注意不同點
{
	return x*2;
}

2、函式的預設引數

      C++函式的原型中可以宣告一個或多個帶有預設值的引數。如果呼叫函式時,省略了相應的實際引數,那麼編譯器就會把預設值作為實際引數。可以這樣來宣告具有預設引數的C++函式原型:
#include iostream.h

void show(int = 1,float = 2.3,long = 6);

int main()
{
	show();
	show(2);
	show(4,5.6);
	show(8,12.34,50L);
	
	return 0;
}

void show(int first,float second,long third)
{
	cout << first =<< first
		<< second =<< second
		<< third =<< third << endl;
}

上面例子中,第一次呼叫show()函式時,讓編譯器自動提供函式原型中指定的所有預設引數,第二次呼叫提供了第一個引數,而讓編譯器提供剩下的兩個,第三次呼叫則提供了前面兩個引數,編譯器只需提供最後一個,最後一個呼叫則給出了所有三個引數,沒有用到預設引數。

六、函式過載

在C++中,允許有相同的函式名,不過它們的引數型別不能完全相同,這樣這些函式就可以相互區別開來。而這在C語言中是不允許的。

1、引數個數不同

#include iostream.h

void a(int,int);
void a(int);

int main()
{
	a(5);
	a(6,7);
	
	return 0;
}


void a(int i)
{
	cout << i << endl;  //輸出5
} 


void a(int i,int j)
{
	cout << i << j << endl;       //輸出67
}

2.引數格式不同

#include iostream.h

void a(int,int);
void a(int,float);

int main()
{
	a(5,6);
	a(6,7.0);

	return 0;
}

void a(int i,int j)
{

	cout << i << j <<endl;          //輸出56
} 

void a(int i,float j)
{
	cout << i << j << endl;          //輸出67.0

}

七、變數作用域      

C++語言中,允許變數定義語句在程式中的任何地方,只要在是使用它之前就可以;而C語言中,必須要在函式開頭部分。而且C++允許重複定義變數,C語言也是做不到這一點的。看下面的程式:

#include iostream.h

int a;

int main()
{
	cin >> a;
	for(int i = 1;i <= 10; i++) //C語言中,不允許在這裡定義變數
	{
		static int a = 0; //C語言中,同一函式塊,不允許有同名變數
		a += i;
		cout<<::a<< <<a<<endl;
        }		
	return 0;
}

八、new和delete運算子

在C++語言中,仍然支援malloc()和free()來分配和釋放記憶體,同時增加了new和delete來管理記憶體。

1.為固定大小的陣列分配記憶體

#include iostream.h

int main()
{
	int *birthday = new int[3];
	birthday[0] = 6;
	birthday[1] = 24;
	birthday[2] = 1940;
	cout << I was born on
		<< birthday[0] << '/' << birthday[1] << '/' << birthday[2] << endl;
	delete [] birthday;      //注意這兒
	
	return 0;
}

在刪除陣列時,delete運算子後要有一對方括號。

2.為動態陣列分配記憶體

#include iostream.h
#include stdlib.h

int main()
{
	int size;
	cin >> size;
	int *array = new int[size];
	for(int i = 0;i < size;i++)
		array[i] = rand();
        for(i = 0;i < size;i++)
		cout << '\n' << array[i];
        delete [] array;
	
	return 0;
}

九、引用型變數

在C++中,引用是一個經常使用的概念。引用型變數是其他變數的一個別名,我們可以認為他們只是名字不相同,其他都是相同的。

1.引用是一個別名

C++中的引用是其他變數的別名。宣告一個引用型變數,需要給他一個初始化值,在變數的生存週期內,該值不會改變。& 運算子定義了一個引用型變數:

int a;

int& b=a;

先宣告一個名為a的變數,它還有一個別名b。我們可以認為是一個人,有一個真名,一個外號,以後不管是喊他a還是b,都是叫他這個人。同樣,作為變數,以後對這兩個識別符號操作都會產生相同的效果。

#include iostream.h

int main()
{
	int a = 123;
	int& b = a;
	cout << a << ','<< b << endl;       //輸出123,123
	a++;
	cout << a << ','<< b << endl;       //輸出124,124
	b++;
	cout << a<< ',' << b << endl;        //輸出125,125

	return 0;
}
2.引用的初始化

和指標不同,引用變數的值不可改變。引用作為真實物件的別名,必須進行初始化,除非滿足下列條件之一:

(1) 引用變數被宣告為外部的,它可以在任何地方初始化

(2) 引用變數作為類的成員,在建構函式裡對它進行初始化

(3) 引用變數作為函式宣告的形參,在函式呼叫時,用呼叫者的實參來進行初始化

3.作為函式形參的引用

引用常常被用作函式的形參。以引用代替拷貝作為形參的優點:

引用避免了傳遞大型資料結構帶來的額外開銷

引用無須象指標那樣需要使用*和->等運算子

#include iostream.h

void func1(s p);
void func2(s& p);

struct s
{
	int n;
	char text[10];
};

int main()
{
	static s str = {123,China};
	func1(str);
	func2(str);
	return 0;
}

void func1(s p)
{
	cout << p.n << endl;
	cout << p.text << endl;
}


void func2(s& p)
{
	cout << p.n << endl;
	cout << p.text << endl;
}
從表面上看,這兩個函式沒有明顯區別,不過他們所花的時間卻有很大差異,func2()函式所用的時間開銷會比func2()函式少很多。它們還有一個差別,如果程式遞迴func1(),隨著遞迴的深入,會因為棧的耗盡而崩潰,但func2()沒有這樣的擔憂。

4.以引用方式呼叫

當函式把引用作為引數傳遞給另一個函式時,被呼叫函式將直接對引數在呼叫者中的拷貝進行操作,而不是產生一個區域性的拷貝(傳遞變數本身是這樣的)。這就稱為以引用方式呼叫。把引數的值傳遞到被呼叫函式內部的拷貝中則稱為以傳值方式呼叫。
#include iostream.h

void display(const Date&,const char*);
void swapper(Date&,Date&);

struct Date
{
	int month,day,year;
};

int main()
{
	static Date now={2,23,90};
	static Date then={9,10,60};
	display(now,Now: );
	display(then,Then: );
	swapper(now,then);
	display(now,Now: );
	display(then,Then: );
	
	return 0;
}

void swapper(Date& dt1,Date& dt2)
{
	Date save;
	save=dt1;
	dt1=dt2;
	dt2=save;
}

void display(const Date& dt,const char *s)
{
	cout << s;
	cout << dt.month << '/' << dt.day << '/'<< dt.year << endl;
}

5.以引用作為返回值

#include iostream.h

struct Date
{
	int month,day,year;
};

Date birthdays[]=
{
	{12,12,60};
	{10,25,85};
	{5,20,73};
};

const Date& getdate(int n)
{
	return birthdays[n-1];
}

int main()
{
	int dt=1;
	while(dt!=0)
	{
		cout<<Enter date # (1-3,0 to quit)<<endl;
		cin>>dt;
		if(dt>0 && dt<4)
		{
			const Date& bd = getdate(dt);
			cout << bd.month << '/' << bd.day << '/'<< bd.year << endl;
		}
	}
	return 0;
}