天天看點

第六課 C++探索“程式”一、頭檔案和源檔案的差別 二、頭檔案的重複包含問題二、程式生成過程三、宏四、内聯函數

一、頭檔案和源檔案的差別

1.頭檔案:變量、函數的聲明,類的定義、宏的定義......

源檔案:變量的定義,函數的定義實作

2.單獨的頭檔案不參與編譯,源檔案參與編譯

int a;//變量的定義
int b=10;//變量的定義

extern int c;//變量的聲明
           

若在頭檔案中定義變量,當多個源檔案包含同一個頭檔案時,會出現變量重定義問題

void fun();//函數的聲明
//--------------------

void fun() //函數的定義
{
    cout<<"fun()"<<endl;
}
           

類在頭檔案中聲明,在源檔案中初始化化時需要添加作用域

//AA.h

class CTest
{
public:
    int m_a;
    const int m_b;//常量在初始化參數清單進行初始化
    
    static int m_c;//類内定義類外初始化化,但不能在頭檔案中初始化,會出現重定義,應該在源檔案中初始化

    CTest();
    ~CTest();

    void fun();
    static void fun2();
    void fun3() const;
    virtual void fun4();
}

//------------------

//AA.cpp

int CTest::m_c=30;//靜态函數在源檔案中初始化
CTest::CTest():m_a(10),m_b(20)
{
    cout<<"m_a="<<m_a<<endl;
}

CTest::~CTest(){
    cout<<"~CTest()"<<endl;
}

void CTest::fun(){
    cout<<"fun()"<<endl;
}

//static要去掉
void CTest::fun2(){
    cout<<"fun2"<<endl;
}

//const 要保留
void CTest::fun3() const{
    cout<<"fun3"<<endl;
}

//virtual要去掉
void CTest::fun4(){
    cout<<"fun3"<<endl;
}
           

 二、頭檔案的重複包含問題

#pragma once//和編譯器溝通,告訴編譯器,目前頭檔案在源檔案中隻包含一次
           

另一種方法是基于宏的邏輯判斷

#ifndef __宏__
#define __宏__

...頭檔案中的代碼...


#endif

//宏的名字與頭檔案名進行綁定,例如AA.h->__AA_H__
           

對比:

1.#pragma once 效率高,代碼編譯效率快

2.#pragma once不用擔心宏重名問題

二、程式生成過程

1.預處理:源代碼檔案main.cpp  ->  main.i預處理後的檔案

(1)#include 頭檔案的展開

(2)删除注釋

(3)#define宏的替換

(4)#ifdef、#ifndef、#endif、#if、#else、#elif等預處理指令處理

2.編譯階段:将預處理後的檔案進一步處理形成彙編檔案,包含彙編代碼,預處理後的檔案main.i  ->  main.asm(彙編檔案)

對代碼進行,語義分析、文法分析、詞法分析和優化

3.彙編階段:将彙編檔案按照彙編代碼一步步生成目标機器指令,從main.asm  ->  main.obj(目标機器檔案(二進制檔案))

4.連結階段:通過連結器将生成的目标機器檔案和庫檔案進行連結整合到一起,形成可執行檔案(.exe(二進制檔案))

1.編譯期:将源代碼交給編譯器,進行編譯生成可執行檔案的過程

2.運作期:将可執行檔案交給作業系統運作,直到程式運作結束的過程

通路修飾符和類的作用域屬于編譯期的概念

類的對象(執行個體化),以及指針引用等屬于運作期的概念

虛函數不能是私有的:因為多态是運作期多态,通路修飾符是編譯期存在的

三、宏

//AA.h
//----------------
#define AA 10

// \連接配接目前行和下一行的代碼,通常最後一行不加\,\後面不能加任何東西包括空格、tab、注釋
#define BB for(int i=0;i<10;i++){\
	cout<<i<<endl;\
}

//宏可以加參數
#define CC(COUNT) for(int i=0;i<COUNT;i++){\
	cout<<i<<endl;\
}

//宏隻是替換,不會做表達式計算求解
#define DD(A,B) A*B


//--------------------------
//main.cpp

    cout<<AA<<endl;
	BB
	CC(5)

    cout<<"---------------"<<endl;
	int a=DD(2+3,4);
	cout<<a<<endl;//輸出結果是14
           

“#”的一些特殊用法

#define AA(A) #A  //“#”代表在兩邊加上""變成字元串
           
#define BB(B) #@B //相當于在變量兩邊加上單引号‘’,變成字元
           
#define CC() int a##b=10  //##是連接配接作用,a##b相當于ab
           

可以使用#undef取消宏的使用,限制宏的使用範圍

#undef AA
           

宏的優點:可以代替程式中常用的常量或者表達式,之後如果需要維護,隻需要維護一份就可以

宏的缺點:

1.不友善調試

2.難看出一些問題,可能出現安全問題

3.帶參數的宏不能進行表達式的計算

四、内聯函數

inline是内聯函數的關鍵字,編譯階段進行替換,可以提高函數的運作效率

對于一些簡單的函數來講,可能調用函數的時間比執行時間都長,可以使用内聯函數

缺點:如果程式中多次調用内聯函數會占用大量空間,相當于空間換時間的做法 

inline int add(a,b)
{
    return a+b;
}
           

适用于函數代碼量少,邏輯簡單的函數,對于一些含有for switch while dowhile 的不推薦使用

inline屬于建議性關鍵字,是否使用取決于編譯器的判斷,遞歸函數和虛函數一定不是内聯函數

不太重要的一點:在類内定義并初始化的預設為内聯函數,類内定義類外初始化的預設不是内聯函數

繼續閱讀