一、命名空間
1、namespace的意義
在C/C++中,變量、函數和后面要學到的類都是大量存在的,這些變量、函數和類的名稱將都存在于全
局作用域中,可能會導致很多沖突。使用命名空間的目的是對標識符的名稱進行本地化,以避免命名
沖突或名字污染,namespac關鍵字的出現就是針對這種問題的。
c語言項目類似下面程序這樣的命名沖突是普遍存在的問題,C++引入namespac就是為了更好的解決
這樣的問題:(因為我們設置的變量rand和stdlib.h中的rand函數沖突所以報錯)
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{// 編譯報錯:error C2365: “rand”: 重定義;以前的定義是“函數”printf("%d\n", rand);return 0;
}
2、namespace的定義
1、定義命名空間,需要使用法到namespace關鍵字,后面跟命名空間的名字,然后接?對{}即可,{}中
即為命名空間的成員。命名空間中可以定義變量/函數/類型等。(注意{}后不跟“;”)。
2、namespace本質是定義出?個域,這個域跟全局域各自獨立,不同的域可以定義同名變量,所以下
面的rand不在沖突了。
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{int rand = 0;
}
int main()
{printf("%d\n", rand);return 0;
}
3、C++中域有函數局部域,全局域,命名空間域,類域;域影響的是編譯時語法查找?個變量/函數/
類型出處(聲明或定義)的邏輯,所有有了域隔離,名字沖突就解決了。局部域和全局域除了會影響
編譯查找邏輯,還會影響變量的聲明周期,命名空間域和類域不影響變量聲明周期。
4、namespace只能定義在全局,當然他還可以嵌套定義。
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{int rand = 0;namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}}
int main()
{printf("%d\n", rand);return 0;
}
5、項目工程中多文件中定義的同名namespace會認為是?個namespace,不會沖突。
test.c文件
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include"test.h"
namespace tmp
{int rand = 0;namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}}
int main()
{printf("%d\n", tmp::d);return 0;
}
test.h文件
#pragma once
namespace tmp
{int d = 0;
}
可以發現在.c文件中可以使用tmp空間中的d變量 。
6、C++標準庫都放在?個叫std(standard)的命名空間中。
7、如果想使用全局域中的變量我們可以這么操作:
int a = 3;
int main()
{int a = 0;printf("%d\n", ::a);return 0;
}
這樣printf先打印的是3。
3、命名空間的使用
編譯查找?個變量的聲明/定義時,默認只會在局部或者全局查找,不會到命名空間里面去查找。所以
下面程序會編譯報錯。
namespace tmp
{int d = 0;namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}
}
int main()
{int a = 0;printf("%d\n", d);//未定義標識符dreturn 0;
}
所以我們要使用命名空間中定義的變量/函數,有三種方式:
1、指定命名空間訪問,項目中推薦這種方式。
tmp::d
2、using將命名空間中某個成員展開,項目中經常訪問的不存在沖突的成員推薦這種方式。
namespace tmp
{int d = 0;namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}
}
using tmp::d;
int main()
{int a = 0;printf("%d\n", d);//未定義標識符dreturn 0;
}
3、展開命名空間中全部成員,項目不推薦,沖突風險很大,日常小練習程序為了方便推薦使用。
namespace tmp
{int d = 0;namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}
}
using namespace tmp;
int main()
{int a = 0;printf("%d\n", d);//未定義標識符dreturn 0;
}
二、輸入和輸出
1、 是 Input Output Stream 的縮寫,是標準的輸入、輸出流庫,定義了標準的輸入、輸
出對象。
2、std::cin 是 istream 類的對象,它主要面向窄字符(narrow characters (of type char))的標準輸
入流。
3、std::cout 是 ostream 類的對象,它主要面向窄字符的標準輸出流。
4、std::endl 是?個函數,流插入輸出時,相當于插入?個換行字符加刷新緩沖區。
5、<<是流插入運算符,>>是流提取運算符。(C語言還用這兩個運算符做位運算左移/右移)
6、使用C++輸入輸出更方便,不需要像printf/scanf輸入輸出時那樣,需要手動指定格式,C**++的輸入**
輸出可以自動識別變量類型(本質是通過函數重載實現的),其實最重要的是C++的流能更好的支持自定義
類型對象輸入和輸出。
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{int a = 0;double b = 0.0;char c = '0';namespace tmp1{int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}
}
using namespace tmp;
using namespace std;
int main()
{cin >> a >> b >> c;cout << a << b << c;return 0;
}
7、cout/cin/endl等都屬于C++標準庫,C++標準庫都放在?個叫std(standard)的命名空間中,所以要
通過命名空間的使用方式去用他們。
8、?般日常練習中我們可以using namespace std,實際項目開發中不建議using namespace std。
三、 缺省參數
1、缺省參數的定義以及規定
缺省參數是聲明或定義函數時為函數的參數指定?個缺省值。在調用該函數時,如果沒有指定實參
則采用該形參的缺省值,否則使用指定的實參,缺省參數分為全缺省和半缺省參數。(有些地方把
缺省參數也叫默認參數)。
全缺省就是全部形參給缺省值,半缺省就是部分形參給缺省值。C++規定半缺省參數必須從右往左
依次連續缺省,不能間隔跳躍給缺省值。
帶缺省參數的函數調用,C++規定必須從左到右依次給實參,不能跳躍給實參。
函數聲明和定義分離時,缺省參數不能在函數聲明和定義中同時出現,規定必須函數聲明給缺省
值。
#include <iostream>
using namespace std;
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func(); // 沒有傳參時,使?參數的默認值Func(10); // 傳參時,使?指定的實參return 0;
}
2、全缺省和半缺省
#include <iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
int main()
{Func1();Func1(1);Func1(1, 2);Func1(1, 2, 3);Func2(100);Func2(100, 200);Func2(100, 200, 300);return 0;
}
半缺省不能這么寫:
void Func2(int a = 10, int b, int c = 20)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
//或者
void Func2(int a = 10, int b, int c)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
必須嚴格遵守:
半缺省參數必須從右往左依次連續缺省,不能間隔跳躍給缺省值。
3、缺省參數的實際應用
我們在沒有學c++之前我們實現棧的初始化以及插人時我們是這么寫的:
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;
// 棧頂
void STInit(ST* ps, int n)
{assert(ps);ps->a = (STDataType*)malloc(n * sizeof(STDataType));ps->top = 0;ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{assert(ps);// 滿了, 擴容if (ps->top == ps->capacity){printf("擴容\n");int newcapacity = ps->capacity == 0 ? 4 : ps->capacity* 2;STDataType* tmp = (STDataType*)realloc(ps->a,newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}
如果我們要插入100個數據則就需要不斷的擴容,有效率的損耗,但是我們在學習了缺省參數后我們可以這么寫:
// 棧頂
void STInit(ST* ps, int n = 4)
{assert(ps);ps->a = (STDataType*)malloc(n * sizeof(STDataType));ps->top = 0;ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{assert(ps);// 滿了, 擴容if (ps->top == ps->capacity){printf("擴容\n");int newcapacity = ps->capacity == 0 ? 4 : ps->capacity* 2;STDataType* tmp = (STDataType*)realloc(ps->a,newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}
int main()
{ST a;STInit(&a, 100);//這里不傳100也可以,因為規定必須從左到右依次給實參,不能跳躍給實參。剛好和缺省參數確定位置互補for (int i = 0; i < 100; i++){STPush(&a, i);}return 0;
}
這樣就有效避免了重復開辟空間的問題。
四、函數重載
C++支持持在同?作用域中出現同名函數,但是要求這些同名函數的形參不同,可以是參數個數不同或者
類型不同。這樣C++函數調用就表現出了多態行為,使用更靈活。(但是返回值不同不能作為重載條件,
因為調用時也無法區分,如果返回值和參數類型或者個數同時變化則也為重載條件)。
1、參數類型不同
#include<iostream>
using namespace std;
// 1、參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}
2、參數個數不同
// 2、參數個數不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}
特別的如果上面的用缺省參數那么在不傳參數調用時就會報錯,編譯器不知道調用誰
// 下?兩個函數構成重載
// f()但是調?時,會報錯,存在歧義,編譯器不知道調?誰
void f1()
{cout << "f()" << endl;
}
void f1(int a = 10)
{cout << "f(int a)" << endl;
}
3、參數順序不同(實際上就是參數類型不同)
// 3、參數類型順序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
#include<iostream>
using namespace std;
// 1、參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}
// 2、參數個數不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, 'a');f('a', 10);return 0;
}
`#include<iostream>
using namespace std;
// 1、參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}
// 2、參數個數不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, 'a');f('a', 10);return 0;
}``
結果: