1. 程式人生 > 其它 >初識C++01:初探C++

初識C++01:初探C++

初探C++

c++介紹

c++支援面向過程程式設計(如c),面向物件程式設計(OOP)和泛型程式設計;

c/c++編譯器比較多,window下是微軟編譯器cl.exe,Linux機下是GCC編譯器,mac下是Clang編譯器(是xcode預設編譯器);可以瞭解一個編譯器和LLVM架構的關係什麼是LLVM

類和物件的介紹

c++中的類是一種構造型別,可以參考一下c的結構體,但是類中進行一些擴充套件,類成員是變數或者是函式。通過類宣告的變數叫物件。

可以類比:類是一張圖紙,不佔用記憶體空間,物件才是具體的實物,佔用記憶體空間,圖紙生成實物的過程是例項化,物件也叫一個例項(Instance),類的成員變數叫屬性

(Property),類的成員函式叫方法(Method)

什麼是面向物件程式設計:

先說一下面向過程:它是一個步驟化的過程,是通過函式一步一步實現這些功能,依此呼叫;

而什麼是面向物件:是一個行為化的過程,把整個需求按照特點功能劃分,將具有共性的部分抽象成類。

舉個例子:

比如設計一個點餐系統,如果是面向過程,會有①客戶點單給服務員;②服務員下單;③客戶支付等一系列操作,然後還有其他客戶下單等;

而面向物件就會將事物抽象成各種類

優點:面向過程不易於維護,複用和擴充套件,而面向物件易維護複用和擴充套件。面向物件由封裝、繼承、多型特性,設計出的東西低耦合、更靈活。

c語言中,每個功能的程式碼都封裝成一個函式,多個函式放在一個原始檔,而C++中多了一層封裝,就是類,類中關聯了相關的變數和函式,更抽象,耦合度更低,更方便我們組織和管理程式碼,快速梳理程式設計思路。

Linux中編譯連結c++方式:

gcc main.cpp -lstdc++ //方式一
g++ main.cpp -o demo //方式二,指定可執行檔名稱(可忽略)

名稱空間

為了解決合作開發時的命名衝突問題,C++ 引入了名稱空間(Namespace)的概念

namespace Li{  //小李的變數定義
    FILE fp = NULL;
}
namespace Han{  //小韓的變數定義
    FILE fp = NULL
}
Li::fp = fopen("one.txt", "r");  //使用小李定義的變數 fp
Han::fp = fopen("two.txt", "rb+");  //使用小韓定義的變數 fp

其中::是一個新符號,稱為域解析操作符,在C++中用來指明要使用的名稱空間

除了直接使用域解析操作符,還可以採用 using 關鍵字宣告

方式一:
using Li::fp;//出現fp都是Li這個空間的
fp = fopen("one.txt", "r");  //使用小李定義的變數 fp
Han :: fp = fopen("two.txt", "rb+");  //使用小韓定義的變數 fp

方式二:
using namespace Li;//如果有未具體指定名稱空間的變數產生了命名衝突,那麼預設採用名稱空間 Li 中的變數。
fp = fopen("one.txt", "r");  //使用小李定義的變數 fp
Han::fp = fopen("two.txt", "rb+");  //使用小韓定義的變數 fp

標頭檔案和std名稱空間

可以發現,對於不帶.h的標頭檔案,所有的符號都位於名稱空間 std 中,使用時需要宣告名稱空間 std;對於帶.h的標頭檔案,沒有使用任何名稱空間,所有符號都位於全域性作用域。這也是 C++ 標準所規定的。

將 std 直接宣告在所有函式外部,這樣雖然使用方便,但在中大型專案開發中是不被推薦的,這樣做增加了命名衝突的風險,推薦在函式內部宣告 std。

cout 和 cin 都是 C++ 的內建物件,而不是關鍵字。C++ 庫定義了大量的類(Class),程式設計師可以使用它們來建立物件,cout 和 cin 就分別是 ostream 和 istream 類的物件,只不過它們是由標準庫的開發者提前建立好的,可以直接拿來使用。這種在 C++ 中提前建立好的物件稱為內建物件。

基本資料型別

介紹一下c++的bool和BOOL:

bool:布林型別,true(1)和false(0),只佔用一個位元組

BOOL:int型別,true(非0)和false(0),佔用大小按具體環境

Const關鍵字

在C語言中,const 用來限制一個變數,表示這個變數不能被修改,我們通常稱這樣的變數為常量(Constant),c++多了兩個特性

① C語言對 const 的處理和普通變數一樣,會到記憶體中讀取資料;C++ 對 const 的處理更像是編譯時期的#define,是一個值替換的過程,不會去記憶體中讀取值

int main(){
    const int n = 10;
    int *p = (int*)&n;  //必須強制型別轉換
    *p = 99;  //修改const變數的值
    printf("%d\n", n);
    return 0;
}
//像這樣,c輸出是99,c++也可以獲取n的地址,但是輸出還是10;

② C和C++中全域性 const 變數的作用域相同,都是當前檔案,不同的是它們的可見範圍:C語言中 const 全域性變數的可見範圍是整個程式,在其他檔案中使用 extern 聲明後就可以使用;而C++中 const 全域性變數的可見範圍僅限於當前檔案,在其他檔案中不可見,所以它可以定義在標頭檔案中,多次引入後也不會出錯

new和delete運算子

C++中,malloc和free這兩個函式仍然可以使用,但是C++又新增了兩個關鍵字,new 和 delete:new 用來動態分配記憶體,delete 用來釋放記憶體。new 也是在堆區分配記憶體,必須手動釋放,否則只能等到程式執行結束由作業系統回收,不要和C語言中 malloc()、free() 一起混用

new和delete的底層實現

int *p = (int*) malloc( sizeof(int) * 10 );  //分配10個int型的記憶體空間
int *p = new int[10];  //分配10個int型的記憶體空間,new 操作符會根據後面的資料型別來推斷所需空間的大小

inline行內函數

函式呼叫是有時間和空間開銷的。程式在執行一個函式之前需要做一些準備工作,要將實參、區域性變數、返回地址以及若干暫存器都壓入棧中,然後才能執行函式體中的程式碼;函式體中的程式碼執行完畢後還要清理現場,將之前壓入棧中的資料都出棧,才能接著執行函式呼叫位置以後的程式碼。

為了消除函式呼叫的時空開銷,C++ 提供一種提高效率的方法,即在編譯時將函式呼叫處用函式體替換,類似於C語言中的巨集展開(替換)。這種在函式呼叫處直接嵌入函式體的函式稱為行內函數(Inline Function)。又稱內嵌函式或者內建函式,一般只將那些短小的、頻繁呼叫的函式宣告為行內函數(編譯後體積小,而且如果採用呼叫函式的話時空開銷比函式程式碼還大)

要在函式定義處新增 inline 關鍵字,而且一般直接在宣告處定義;

#include <iostream>
using namespace std;
//行內函數,交換兩個數的值
inline void swap(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
int main(){
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap(&m, &n);
    cout<<m<<", "<<n<<endl;
    return 0;
}

行內函數主要有兩個作用,一是消除函式呼叫時的開銷,二是取代帶引數的巨集

因為巨集只是字元替換,帶引數會出現意想不到的錯誤;

#include <iostream>
using namespace std;
#define SQ(y) y*y
int main(){
    int n, sq;
    cin>>n;
    sq = SQ(n+1);
    cout<<sq<<endl;
    return 0;
    //輸入:9   輸出結果:19
}

sq = SQ(n+1);在巨集展開後會變為sq = n+1*n+1,字元直接替換成9;

還要看如何規範使用行內函數,看記憶體問題和標頭檔案問題;

函式的預設引數

C++規定,預設引數只能放在形參列表的最後,而且一旦為某個形參指定了預設值,那麼它後面的所有形參都必須有預設值。

void func(int a, int b=10, int c=20){ }
void func(int a, int b, int c=20){ }
//錯誤例子:
void func(int a, int b=10, int c=20, int d){ }
void func(int a, int b=10, int c, int d=20){ }

//在同一檔案中可以多次函式宣告,但在給定的作用域中一個形參只能被賦予一次預設引數,而且要賦值的引數右側都有預設值
//多次宣告同一個函式
void func(int a, int b, int c = 36);
void func(int a, int b = 5, int c);

C++ 規定,在給定的作用域中只能指定一次預設引數,所以同一檔案中預設引數宣告和定義不能同時帶有同一個位置的預設引數;

函式過載

C++ 允許多個函式擁有相同的名字,只要它們的引數列表不同就可以,這就是函式的過載(Function Overloading);

引數列表又叫引數簽名,包括引數的型別、引數的個數和引數的順序,只要有一個不同就叫做引數列表不同,但是隻是引數名稱不同不可以叫函式過載,僅僅返回型別不同也不可以

C++程式碼在編譯時會根據引數列表對函式進行重新命名,例如void Swap(int a, int b)會被重新命名為_Swap_int_intvoid Swap(float x, float y)會被重新命名為_Swap_float_float。當發生函式呼叫時,編譯器會根據傳入的實參去逐個匹配,以選擇對應的函式,如果匹配失敗,編譯器就會報錯,這叫做過載決議(Overload Resolution)

本質上:它們還是不同的函式,佔用不同的記憶體,入口地址也不一樣;

過載的二義性問題