命名空間
命名空間定義
定義命名空間,需要使用到namespace關鍵字,後面跟命名空間的名字,然後接一對{}即可,{} 中即爲命名空間的成員
命名空間的使用
1.命名空間成員訪問:命名空間名 + ::+ 變量名/函數名
例1:
namespace N1 {
int b;
int sub(int a, int b) {
return a - b;
}
namespace N2 {
int b;
}
}
void test1() {
N1::b = 10;
N1::sub(10, 5);
//嵌套
N1::N2::b = 5;
}
2.命名空間成員訪問:using + 命名空間名 +::+ 變量名/函數名
使用時不需要再加作用域,但在函數外部聲明
例2:
namespace N1 {
int b;
int sub(int a, int b) {
return a - b;
}
namespace N2 {
int b;
}
}
using N1::b;
using N1::sub;
using N1::N2::b;
void test1() {
b = 10;
sub(10,5);
b = 5;
}
3.引入整個命名空間:using space + 命名空間名
例3:
namespace N1 {
int b;
int sub(int a, int b) {
return a - b;
}
namespace N2 {
int b;
}
}
using namespace N1;
void test1() {
b = 10;
sub(10,5);
b = 5;
}
注意:不推薦用這種方式,可能會造成變量重複(污染)。
C++輸入輸出
例4:
#include<iostream>
void testIO() {
printf("!");
int a = 0;
double b = 0;
//對比c語言
//scanf("%d",&a);
//scanf("%lf",&b);
//輸入
std::cin >> a;
std::cin >> b;
//輸出
std::cout << a;
std::cout << '\n';
std::cout << b;
}
int main() {
testIO();
return 0;
}
例5:
#include<iostream>
using namespace std;
void testIO() {
int a = 0;
double b = 0;
cin >> a >> b;
cout << a << ' ' << b << endl;
}
int main() {
testIO();
return 0;
}
從上例可以看到:
c++的輸入不需要格式,相比c語言更加精簡,面向對象
cin,cout從左向右連續輸入輸出
缺省參數
概念
缺省參數是聲明或定義函數時爲函數的參數指定一個默認值。在調用該函數時,如果沒有指定實 參則採用該默認值,否則使用指定的實參。
使用
- 沒有傳參時,使用參數的默認值
- 傳參時,使用指定的實參
例六:
void fun(int a = 100, int b = 0) {
cout << a << ' ' << b << endl;
}
int main() {
fun();
fun(1, 2);
return 0;
}
//輸出:
//100 0
//1 2
半缺省參數與全缺省參數
例5例6都爲全缺省參數型
例7.半缺省參數使用
void fun(int a, int b = 0) {
cout << a <<' '<< b << endl;
}
int main() {
fun(1, 2);
fun(1);
}
//輸出:
//1 2
//1 0
- 半缺省參數:缺省值從右到左連續賦值
即:缺省值應該在非缺省值的右邊
- 缺省值只能在一個地方定義(聲明中或者定義中)
例8.
// 非缺省值在左端 缺省值在右邊
void fun(int a, int b = 0, int c = 0) {
cout << a << ' ' << b << ' ' << c << endl;
}
int main() {
fun(1, 2, 3);
fun(1, 2);
fun(1);
}
例9
void fun(int a = 5) {
;
}
//或
void fun(int a = 0);
void fun(int a) {//這時不能在定義中再次定義這個缺省參數
;
}
缺省值必須時常量或者全局變量
函數重載
函數重載是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數
這些 同名函數的形參列表(參數個數 或 類型 或 類型順序)必須不同,常用來處理實現功能類似數據類型不 同的問題
例10
//參數類型不同
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int main() {
cout << add(1, 2) << ' ' << add(1.2, 2.0) << endl;
}
//輸出
//3 3.2
//參數個數不同
int add(int a, int b, int c) {
return a + b + c;
}
int add(int a, int b) {
return a + b;
}
int main() {
cout << add(1, 2, 3) << ' ' << add(1, 2) << endl;
}
//輸出
//6 3
//參數類型順序不同
void Print(char c, int a) {
cout << c << ' ' << a << endl;
}
void Print(int a, char c) {
cout << a <<' '<< c << endl;
}
int main() {
Print('a', 1);
Print(1, 'a');
}
//輸出
//a 1
//1 a
總結:
- 函數名相同
- 返回值不同不構成函數重載
- 作用域不同不構成函數重載
判斷:以下函數構成函數重載嗎?
short Add(short left, short right)
{
return left+right;
}
int Add(short left, short right) {
return left+right;
}
爲什麼c++可以有函數重載而c語言沒有呢? 這就牽扯到了下個知識點——名字修飾
名字修飾
首先我們看一下函數名在c與c++底層的不同
再看這一組:
vs編譯器:
函數名編譯:sub(int a,int b)
c底層:_sub -----------> _sub
c++底層:?sub@@YAHHH@Z ---------> ?sub@@YAHMH@Z
由此可知:
C語言不支持函數重載是因爲C語言的函數名修飾規則不支持,因爲只要函數名相同,底層的函數名就是相同的,從而無法區分。
而C++的底層函數名修飾規則要複雜的多,它由前綴+函數名+參數信息構成,從而即使函數名相同,底層的函數名是不同的,這樣這些重載的函數時可以區分的。
extern "C"{}
有時候在C++工程中可能需要將某些函數按照C的風格來編譯,在函數前加extern "C",意思是告 訴編譯器,將該函數按照C語言規則來編譯
以下兩個函數是函數重載嗎? (不是)
void TestFunc(int a = 10)
{
cout<<"void TestFunc(int)"<<endl;
}
void TestFunc(int a) {
cout<<"void TestFunc(int)"<<endl;
}
引用
概念
引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會爲引用變量開闢內存空 間,它和它引用的變量共用同一塊內存空間
使用
類型& 引用變量名(對象名) = 引用實體
1. 引用在定義時必須初始化
2. 一個變量可以有多個引用
3. 引用一旦引用一個實體,再不能引用其他實體
4.引用類型必須和引用實體是同種類型的
例11
void testRef() {
int a = 10;
int copy = a;
int& ra = a;
ra = 100;
copy = 1000;
cout << a << endl;
}
//輸出
//100
int main() {
testRef();
}
void testRef() {
int a = 10;
int b = 20;
int& ra = a;
int& rra = a;
int& rrra = a;
ra = 100;
cout << a << endl;
rra = 1000;
cout << a << endl;
rrra = 10000;
cout << a << endl;
ra = b;
cout << a << endl;//改變的是ra的值也就是a的值
}
//輸出
//100
//1000
//10000
//說明一個變量可以有多個引用
int main() {
testRef();
return 0;
}
引用是變量的別名
常引用
void testRef() {
int c = 10;
const int& rc = 10;//指向常量
const int d = 5;
const int& rd = d;
float f = 2.0;
const int& rf = f;
//浮點數賦值給整形時會進行隱式類型轉換,2.0 轉化爲 2,
//這個2是一個臨時變量,它無法被修改,因此它具有常量的性質,因此需要加上const指向這個常量
}
int main() {
testRef();
return 0;
}
引用實例
例12
void Swap(int* a, int* b) {
int tmp = 0;
tmp = *a;
*a = *b;
*b = tmp;
}
void Swap(int& a, int& b) {
int tmp = 0;
tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 1;
int b = 2;
Swap(&a, &b);
Swap(a, b);
printf("%d %d", a, b);
return 0;
}
//輸出
//1 2
//證明引用有與指針同樣的功能
引用類型返回值
int& SelfAdd(int& a) {
a++;
return a;
}
int main() {
int a = 0;
int& ra = SelfAdd(a);
cout << a << endl;
return 0;
}
//輸出
//1
注意:
請看下面的代碼:你覺他它會輸出5嗎?
int& add(int& a, int& b) {
int c = a + b;
return c;
}
int main() {
int a = 2;
int b = 3;
int& sum = add(a, b);
cout << a << ' + ' << b << ' = ' << sum << endl;
}
//輸出一個任意值
爲什麼會這樣?請看下圖
所以我們在使用引用時要注意引用的變量的生命週期要大於函數的生命週期
改爲:
int& add(int& a, int& b,int& c) {
c = a + b;
return c;
}
int main() {
int a = 2;
int b = 3;
int c = 0;
int& sum = add(a, b,c);
cout << a << ' + ' << b << ' = ' << sum << endl;
}
效率問題:
我們看一下下面這個代碼,大家猜一猜他們倆的處理時間會相差多少?
struct A {
int arr[100000];
};
void testValue(A a) {
}
void testRef(A& ra) {
}
int main(){
A a;
int i = 0;
size_t begin,end;
begin = clock();
//因爲程序跑的太快,所以我們用循環來增加他們跑的時間方便我們更具體的觀察比較
for(i = 0;i<1000000;i++)
testValue(a);
end = clock();
cout << "testValue :" << (end - begin)/CLOCKS_PER_SEC << endl;
begin = clock();
for (i = 0; i < 1000000; i++)
testRef(a);
end = clock();
cout << "testRef :" << (end - begin)/ CLOCKS_PER_SEC << endl;
//(end - begin)/ CLOCKS_PER_SEC 計算程序運行的秒數
}
//輸出
//testValue:25
//testRef : 0
引用和指針的區別
引用用指針實現,本質就是指針
引用和指針的不同點:
1. 引用在定義時必須初始化,指針沒有要求
2. 引用在初始化時引用一個實體後,就不能再引用其他實體,而指針可以在任何時候指向任何 一個同類型實體
3. 沒有NULL引用,但有NULL指針
4. 在sizeof中含義不同:引用結果爲引用類型的大小,但指針始終是地址空間所佔字節個數(32 位平臺下佔4個字節)
5. 引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小
6. 有多級指針,但是沒有多級引用
7. 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理
8. 引用比指針使用起來相對更安全
以上就是本次的所有內容,有什麼不足歡迎指教!
對您有幫助不妨點一個贊!