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;
}