1. 程式人生 > >C++學習筆記——名稱空間&預設引數&函式過載&引用

C++學習筆記——名稱空間&預設引數&函式過載&引用

C++學習筆記——名稱空間&預設引數&函式過載&引用

C++:

1.解決C語言中設計不好或者使用不是很方便的語法—>優化 2.增加新的語法特性 注:extern “C”:在C++工程中,將程式碼按照C語言的風格來編譯

C++關鍵字 (C++98)----63

在這裡插入圖片描述

名稱空間:

用於解決名字衝突,相當於一個作用域

namespace N1 { 1.變數 2.函式 3.名稱空間(巢狀) }

名稱空間的定義方式: 1.普通名稱空間(只包含變數和函式) 2.名稱空間可以巢狀 3.可以建立多個相同名字的名稱空間–>合併

名稱空間的訪問方式: 1.在成員前+ N:: (N為名稱空間的名字,::為作用域限定符—預設訪問全域性的) 2.使用using來指定訪問變數:using N2::N3::a; 3.使用using來指定訪問名稱空間:using namespace N2;

標準輸入/輸出

使用標準輸入cin和標準輸出cout時,必須包含**標頭檔案以及std標準名稱空間** //為了和c區分,c++98之後標頭檔案不需要包含.h // using namespace std;標準名稱空間

cin標準輸入(鍵盤):int a = 0; double b = 12.34; cin>>a>>b; cout標準輸出(控制檯):cout<<10<<" "<<12.34<<endl; //好處是不需要加資料格式控制(%d,%c之類) (dec:十進位制 oct:八進位制 hex:十六進位制 二進位制可以使用bitset<>

把要輸出的數變成二進位制儲存輸出)

“備胎”–>預設引數

概念:宣告或定義函式時為函式的引數指定一個預設值(呼叫時如果沒有指定實參就會使用預設值)

全預設引數:所有引數都有預設值 對於全預設引數,如果呼叫函式時只傳遞了一部分實參,則實參從左往右傳遞,其餘採用預設值 半預設引數:部分引數帶有預設值,必須從右向左依次給出 對於半預設引數,要注意對沒有給出預設值的引數傳遞實參,實參同樣從左往右傳遞

注意: 1.半預設引數必須從右往左依次給出,不能間隔著給 2.預設引數不能在函式宣告和定義中同時出現(為了避免出現宣告和定義不一致情況),最好在宣告的位置 3.預設值必須是常量或者全域性變數 4.C語言不支援(編譯器不支援)

“一詞多義”–>函式過載:

概念:是函式的一種特視情況,C++允許在同一作用域中宣告幾個功能類似的同名函式,但這些同名函式的形參列表(引數個數、型別、順序)必須不同,常用來處理功能類似資料型別不同的問題 //與返回值型別無關,如果只是返回值型別不同,則不能構成過載

二義性無參函式同名的全預設函式不能同時存在

C語言中不支援函式過載是因為: C語言中編譯器對函式名字的修飾規則:只是簡單地在函式名字前新增下劃線 C++中支援函式過載是因為: //在vs中通過只給宣告不給定義的方式呼叫函式,編譯成功,連結時報錯就可以看到編譯器對函式名字的修飾規則↓↓↓ C++中編譯器對函式名字的修飾規則(_cdecl:C語言預設呼叫約定): int ADD(int left,int right); —> [email protected]@[email protected] ?函式名@@YA引數表@Z 引數表(返回值和形參型別)符號表示: void - X int - H unsigned int - I float - M double - N bool - N char - D short - F long - J unsigned long - K

“外號”–>引用

概念給已存在的變數取一個別名,編譯器不會為引用變數開闢記憶體空間,它和它引用的變數共用同一塊記憶體空間

型別& 引用變數名(物件名)= 引用實體

引用特性: 1.引用在定義時必須初始化 2.一個變數可以有多個引用 3.引用一旦引用了一個實體,就不能再引用其他實體

注意: 1.引用型別必須與引用實體同類型的 2.引用常量實體時要加const修飾 3.一般情況下,因為引用與實體共用同一塊記憶體空間,所以改變引用的值也就是改變了實體的值 4.引用型別與引用實體不同時加const可以通過編譯,此時編譯器會為引用建立一個臨時變數,這個臨時變數具有常屬性

使用場景: 1.作形參 如果不需要通過形參修改實參的值,最好的方法是在形參引用前加上const修飾 2.作返回值 如果用引用作為函式的返回值型別不能返回函式棧上的空間 在這裡插入圖片描述 如果一定要用引用作為返回值,返回的變數生命週期一定要比函式的生命週期長 比如可以這樣稍作修改: 在這裡插入圖片描述

傳值、傳地址、傳引用效率比較

#include <Windows.h>
struct A
{
    int array[10000];
};
void TestFunc(A& a)
{}
void TestRefPtr()
{
    A a;
    size_t start = GetTickCount();
    for (size_t i = 0; i < 1000000; i++)
        TestFunc(a);
    size_t end = GetTickCount();
    cout << end - start << endl;
}
int main()
{
    TestRefPtr();
    system("pause");
    return 0;
}

通過上面程式碼的比較,我們發現引用和指標在傳參上的效率幾乎相同

引用與指標的區別在這裡插入圖片描述 不同點: 1.引用在定義時必須初始化,指標沒有要求(但最好有一個合法的指向) 2.引用在初始化時引用一個實體後,就不能再引用其他實體,而指標可以在任何時候指向任意一個同類型實體 3.沒有NULL引用,但有NULL指標 4.在sizeof中含義不同:引用的結果為引用型別的大小,但指標始終是地址空間所佔的位元組個數(32位平臺為4個位元組) 5.引用自加是引用的實體加1指標自加是指標向後偏移一個型別的大小 6.有多級指標,但沒有多級引用(拓展:C++11中將const int&& rra = 10;這種形式稱為右值引用,將普通引用稱為左值引用) 7.訪問實體的方式不同,指標需要顯示解引用,而引用由編譯器自己處理 總結來說也可以得出; 1.引用更安全。因為指標在使用之前必須要判空,而引用不需要(因為規定了引用在定義時必須初始化) 2.引用更簡潔。引用在使用時程式碼比較清晰,也不需要解引用操作,寫起來簡單,看起來舒服,還可以達到指標的效果

最後附上我的學習程式碼,僅供參考

#include <iostream>
using namespace std;

#if 0
//名稱空間

#include <stdio.h>
#include <stdlib.h>



//普通名稱空間
namespace N1
{
	int a = 10;
	int b = 20;

	int Add(int left,int right)
	{
		return left + right;
	}
}

//名稱空間可以巢狀
namespace N2
{
	int c = 30;
	int d = 40;

	int Sub(int left, int right)
	{
		return left - right;
	}

	namespace N3
	{
		int a = 50;
		int b = 60;

		int Mul(int left, int right)
		{
			return left * right;
		}
	}
}

//可以建立多個相同名字的名稱空間
namespace N1
{
	int Div(int left, int right)
	{
		return left / right;
	}
}

//using N2::N3::a;
using namespace N2;
int main()
{
	/*printf("%d\n", ::a);
	printf("%d\n", N1::a);
	printf("%d\n", N2::N3::a);*/
	//printf("%d\n", a);
	printf("%d\n", Sub(d, c));
	system("pause");
	return 0;
}
#endif

#if 0
//標準輸入/輸出

int main()
{
	int a = 0;
	double b = 12.34;
	cin >> a >> b;
	cout << a <<"	"<< b << endl;
	//cout << hex << a<<endl;
	cout << 10 << "    " << 12.34 << endl;
	cout << "hello world!" << endl;
	system("pause");
	return 0;
}
#endif

#if 0
//預設引數

int g_a = 9;
void TestFunc(int a = g_a)
{
	cout << a << endl;
}

//全預設引數:所有引數都有預設值
void TestFunc1(int a = 0, int b = 1,int c=2)
{
	cout << a << " " << b << " " << c << endl;
}

//半預設引數:部分引數帶有預設值,必須從右向左依次給出
void TestFunc2(int a, int b = 1, int c = 0)
{
	cout << a << " " << b << " " << c << endl;
}

int main()
{
	/*TestFunc();
	TestFunc(10);*/

	/*TestFunc1();
	TestFunc1(10, 20, 30);
	TestFunc1(10);
	TestFunc1(10, 20);*/

	TestFunc2(10);
	TestFunc2(10,20);
	TestFunc2(10,20,30);
	system("pause");
	return 0;
}
#endif

#if 0
//函式過載

int ADD(int left, int right)
{
	return left + right;
}

double ADD(double left, double right)
{
	return left + right;
}

char ADD(char left, char right)
{
	return left + right;
}

//形參列表不同(個數、型別、順序)
void Test()
{}

void Test(int a)
{}

void Test(double a)
{}

void Test(int a, double b)
{}

void Test(double a, int b)
{}

int main()
{
	//cout << ADD(1, 2) << endl;
	//cout << ADD(1.1, 2.2) << endl;
	//cout << ADD('1', '2') << endl;//ASCLL碼相加


	system("pause");
	return 0;
}
#endif

#if 0
//引用

void Swap(int& left,int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}

//如果不需要通過形參修改實參的值,最好的方法是在形參引用前加上const修飾
int TestFunc(const int& a)
{
	return a;
}
//如果用引用作為函式的返回值型別,不能返回函式棧上的空間
//如果一定要用引用作為返回值,返回的變數生命週期一定要比函式的生命週期長
int& Test()
{
	int x = 1;
	return x;
}

int main()
{
	int a = 10;
	int b = 20;
	const int c = 30;
	int& ra = a;//引用在定義時必須初始化
	int& rra = a;//一個變數可以有多個引用
	//int& ra = b; //引用一旦引用了一個實體,就不能再引用其他實體

	cout << &a << endl;//共用同一塊記憶體空間,所以地址都相同
	cout << &ra << endl;
	cout << &rra << endl;

	const int& rc = c;//引用常量實體必須加const修飾
	const int& rd = 10;

	double e = 12.34;
	const int& re = e;//引用型別與引用實體不同時加const可以通過編譯,此時編譯器會為引用建立一個臨時變數,這個臨時變數具有常屬性
	e = 100;

	ra = 20;//一般情況下,因為引用與實體共用同一塊記憶體空間,所以改變引用的值也就是改變了實體的值
	rra = 30;

	a = 10;
	b = 20;
	Swap(a, b);

	cout << TestFunc(a) << endl;

	int& rx = Test();
	cout << rx << endl;//10
	cout << rx << endl;//隨機值 因為第一次輸出時Test函式中x所指向的棧上空間已經被cout壓棧覆蓋了
	system("pause");
	return 0;
}
#endif

//傳值、傳地址、傳引用效率比較:

#include <Windows.h>
struct A
{
	int array[10000];
};

void TestFunc(A& a)
{}

void TestRefPtr()
{
	A a;
	size_t start = GetTickCount();
	for (size_t i = 0; i < 1000000; i++)
		TestFunc(a);

	size_t end = GetTickCount();
	cout << end - start << endl;
}

int main()
{
	int a = 10;
	int* pa = &a;
	*pa = 20;

	int &ra = a;
	ra = 20;

	TestRefPtr();

	ra++;
	pa++;

	char c = 'a';
	char& rc = c;

	char* pc = &c;
	cout << sizeof(rc) << endl;
	cout << sizeof(pc) << endl;

	system("pause");
	return 0;
}