C++ 基礎(chǔ)語法,類,this指針,引用,內(nèi)存布局,構(gòu)造函數(shù)

cin cout

  • C++ 中常使用 cin 、 cout 進行控制臺的輸入、輸出
  • cin 用的右移運算符 cout 用的是左移運算符
  • endl 是換行的意思
  int main() {
    int age;
    cin >> age;
    cout << "age is" << age << endl;
  }

函數(shù)重載

  • 規(guī)則
    • 函數(shù)名相同
    • 參數(shù)個數(shù)不同,參數(shù)類型不同,參數(shù)順序不同
  • 注意
    • 返回值類型與函數(shù)重載無關(guān)
    • 調(diào)用函數(shù)時,實參隱式類型轉(zhuǎn)換可能會產(chǎn)生二義性
  • 本質(zhì)
    • 采用了 name mangling 或者叫 name decoration 技術(shù)
    • C++ 編譯器默認會對符號名(變量名、函數(shù)名等)進行改編、修飾,有些地方翻譯為“命名傾軋”
    • 重載時會生成多個不同的函數(shù)名,不同編譯器( MSVC 、 g++g++)有不同的生成規(guī)則
    • 通過 IDA 打開 【 VS_Release_ 禁止優(yōu)化 】 可以看到

extern “C”

  • extern "C” 修飾的代碼會按照 C 語言的方式去編譯
extern "C" {
  void func() {
    cout << "func()" << endl;
  }

  void func(int a) {
    cout << "func(int a) " << a << endl;
  }
}
  • 如果函數(shù)同時有聲明和實現(xiàn), 要讓函數(shù)聲明被 " extern " 修飾 ,函數(shù)實現(xiàn)可以不修飾

    extern "C" void func();
    extern "C" void func(int a);
    
    extern "C" {
      void func() {
    
      }
    
      void func(int a) {
    
      }
    }
    
    
  • 由于 C 、 C++ 編譯規(guī)則的不同,在 C 、 C++ 混合開發(fā)時,可能會經(jīng)常出現(xiàn)以下操作, C++ 在調(diào)用 C 語言 API 時,需要使用 extern " 修飾 C 語言的函數(shù)聲明

extern-c.png
  • 有時也會在編寫 C 語言代碼中直接使用 extern “C” ,這樣就可以直接被 C++ 調(diào)用

    extern-c1.png

volidate 關(guān)鍵字

  • 不論何時都從內(nèi)存中讀取數(shù)據(jù),不使用編譯器的優(yōu)化后的值

默認參數(shù)

  • C++ 允許函數(shù)設(shè)置默認參數(shù),在調(diào)用時可以根據(jù)情況省略實參。規(guī)則如下:
    • 默認參數(shù)只能按照右到左的順序
    • 如果函數(shù)同時有聲明、實現(xiàn),默認參數(shù)只能放在函數(shù)聲明中
    • 默認參數(shù)的值可以是常量、全局符號(全局變量、函數(shù)名)
  • 函數(shù)重載、默認參數(shù)可能會產(chǎn)生沖突、二義性(建議優(yōu)先選擇使用默認參數(shù))

內(nèi)聯(lián)函數(shù)(inline function)

  • 使用 inline 修飾函數(shù)的聲明或者實現(xiàn),可以使其變成內(nèi)聯(lián)函數(shù)
    • 建議聲明和實現(xiàn)都增加 inline 修飾
  • 特點
    • 編譯器會將函數(shù)調(diào)用直接展開為函數(shù)體代碼
    • 可以減少函數(shù)調(diào)用的開銷
    • 會增大代碼體積
  • 注意
  • 盡量不要內(nèi)聯(lián)超過 10 行代碼的函數(shù)
  • 有些函數(shù)即使聲明為 inline ,也不一定會被編譯器內(nèi)聯(lián),比如遞歸函數(shù)

內(nèi)聯(lián)函數(shù)與宏

  • 內(nèi)聯(lián)函數(shù)和宏,都可以減少函數(shù)調(diào)用的開銷
  • 對比宏,內(nèi)聯(lián)函數(shù)多了語法檢測和函數(shù)特性
  • 思考以下代碼的區(qū)別
#define sum (x) (x +
inline int sum( int x ) { return x + x ;
int a = 10; sum(a++);

引用(Reference)

  • 在 C 語言中,使用指針( Pointer )可以間接獲取、修改某個變量的值
  • 在 C++ 中,使用引用( Reference )可以起到跟指針類似的功能
      int age = 20;
      int &rage = age;
    
  • 注意點
    • 引用相當(dāng)于是變量的別名(基本數(shù)據(jù)類型、枚舉、結(jié)構(gòu)體、類、指針、數(shù)組等,都可以有引用)
    • 對引用做計算,就是對引用所指向的變量做計算
    • 在定義的時候就必須初始化,一旦指向了某個變量,就不可以再改變,“從一而終”
    • 可以利用引用初始化另一個引用,相當(dāng)于某個變量的多個別名
    • 不存在 【 引用的引用、指向引用的指針、引用數(shù)組 】
  • 引用存在的價值之一:比指針更安全、函數(shù)返回值可以被賦值

const

  • const 是常量的意思,被其修飾的變量不可修改
    • 如果修飾的是類、結(jié)構(gòu)體(的指針),其成員也不可以更改
    • 以下 5 個指針分別是什么含義?
      int age = 10;
      const int *p0 = &age;
      int const *p1 = &age;
      int * const p2 = &age;
      const int * const p3 = &age;
      int const * const p4 = &age;
    
    • 上面的指針問題可以用以下結(jié)論來解決:
      • const 修飾的是其右邊的內(nèi)容
     int age = 10;
     int score = 20;
     /*
       *p0 = 20; // 失敗, 不能改變指向變量的值
       p0 = &score; // 成功,可以改變指針的指向
      */
     const int *p0 = &age;
     /*
      *p1 = 20; // 失敗, 不能改變指向變量的值
      p1 = &score; // 成功,可以改變指針的指向
      */
     int const *p1 = &age;
     /*
      *p2 = 20; // 成功, 可以改變改變指向變量的值
      p2 = &score; // 失敗,不能改變指針的指向
      */
     int * const p2 = &age;
     /**
      *p3 = 20; // 失敗, 不能改變改變指向變量的值
      p3 = &score; // 失敗,不能改變指針的指向
      */
     const int * const p3 = &age;
     /**
      *p4 = 20; // 失敗, 不能改變改變指向變量的值
       p4 = &score; // 失敗,不能改變指針的指向
      */
     int const * const p4 = &age;
    
image.png

常引用(Const Reference)

  • 引用可以被 const 修飾,這樣就無法通過引用修改數(shù)據(jù)了,可以稱為常引用

    • const 必須寫在 符號的左邊,才能算是常引用
  • const 引用的特點

    • 可以指向臨時數(shù)據(jù)(常量、表達式、函數(shù)返回值等)
    • 可以指向不同類型的數(shù)據(jù)
    • 作為函數(shù)參數(shù)時( 此規(guī)則也適用于 const 指針
    • 可以接受 const 和非 const 實參(非 const 引用,只能接受非 const 實參)
    • 可以跟非 const 引用構(gòu)成重載
  • 當(dāng)常引用指向了不同類型的數(shù)據(jù)時,會產(chǎn)生臨時變量,即引用指向的并不是初始化時的那個變量

數(shù)組的引用

  • 常見的 2 種寫法
    int array[] = {10, 20, 30};
    int (&ref)[3] = array;
    int * const &ref2 = array;
    

引用的本質(zhì)

  • 就是指針,只是編譯器削弱了它的功能,所以引用就是弱化了的指針
  • 一個引用占用一個指針的大小

  • C++ 中可以使用 struct 、 class 來定義一個類

    • struct 和 class 的區(qū)別
      • struct 的默認成員權(quán)限是 public
      • class 的默認成員權(quán)限是 private
    // 類的定義
    struct Person {
      // 成員變量
      int m_age;
    
      // 成員函數(shù)
      void run() {
        cout << m_age <<"run()" <<endl;
      }
    }
    
    class Person {
    public:
      int m_age;
    
      void run() {
        cout << m_age <<"run()" <<endl;
      }
    }
    
    // 訪問person對象
    Person person;
    person.m_age = 20;
    person.run();
    
    // 通過指針訪問person對象
    Person *p = &person;
    p->m_age = 30;
    p->run();
    
  • 上面代碼中 person 對象、 pPerson 指針的內(nèi)存都是在函數(shù)的??臻g,自動分配和回收的, person對象地地址就是成員變量m_age的地址

  • 可以嘗試反匯編 struct 和 class ,看看是否有其他區(qū)別

  • 實際開發(fā)中,用 class 表示類比較多

對象的內(nèi)存布局

  • 成員變量的類型相同時,對象的內(nèi)存為所有成員變量的內(nèi)存之和
  • 類型不同時,采用內(nèi)存對齊方式計算
  • 空對象占用的內(nèi)存空間為:1:c++編譯器會給每個空對象分配一個字節(jié)空間,是為了區(qū)分空對象占用內(nèi)存的位置
  • 每個空對象也應(yīng)該有一個獨一無二的內(nèi)存地址
// 類的定義
struct Person {
  // 成員變量
  int m_id;
  int m_age;
  int m_height;

  // 成員函數(shù)
  void display() {
    cout << m_id << endl;
    cout << m_age << endl;
    cout << m_height << endl;
  }
}

Person person;
person.m_id = 10;
person.m_age = 20;
person.m_height = 30;

// 創(chuàng)建指針
Person *pPerson = (Person*)&person.m_age; // 指針指向person對象對象的第一個地址
pPerson->m_id = 40; // 相當(dāng)于是person對象的第一個地址賦值,給m_age賦值
pPerson->m_age = 50;

person.display(); //輸出 10 40 50

this

  • this 是指向當(dāng)前對象的指針
  • 對象在調(diào)用成員函數(shù)的時候,會自動傳入當(dāng)前對象的內(nèi)存地址
  • 指向被調(diào)用的成員函數(shù)/變量所屬的對象
  • 當(dāng)形參和成員變量同名時i,可用this指針區(qū)分
  • 在類的非靜態(tài)成員函數(shù)中返回對象本身,可使用 return *this
  • this指針的本質(zhì)是指針常量,指針的指向不可以修改的
struct Person {
  // 成員變量
  int m_id;
  int m_age;
  int m_height;

  // 成員函數(shù)
  void display() {
      cout << "m_id is " << this->m_id << endl;
      cout << "m_age is "  <<  this->m_age << endl;
      cout << "m_height is "  <<  this->m_height << endl;
  }
};

class Perosn {
 public:
     static int m_age;
     
     Perosn& addAge(int age) {
         this->m_age = age;
         return  *this;
     }
 }
 
 void testPerson() {
     Perosn p;
     p.addAge(10).addAge(10).addAge(10);
 }

封裝

  • 成員變量私有化,提供公共的 getter 和 setter 給外界去訪問成員變量
struct Person {
private:
    // 成員變量
    int m_id;
public:
    int m_age;
    int m_height;

    // 成員函數(shù)
    void display() {
        cout << "m_id is " << this->m_id << endl;
        cout << "m_age is "  <<  this->m_age << endl;
        cout << "m_height is "  <<  this->m_height << endl;
    }

    void setId(int id) {
        this->m_id = id;
    }

    int getId() {
       return this->m_id;
    }
};

內(nèi)存空間的布局

memory_zone.png
  • 每個應(yīng)用都有自己獨立的內(nèi)存空間,其內(nèi)存空間一般都有以下幾大區(qū)域
    • 代碼段(代碼區(qū))
      • 用于存放代碼
    • 數(shù)據(jù)段(全局區(qū))
      • 用于存放全局變量等
      • 靜態(tài)變量
      • 存放常量(符串常量,const修飾的全局變量)
  • 棧空間
    • 存放局部變量,函數(shù)參數(shù)
    • 棧區(qū)的數(shù)據(jù)有編譯器管理開辟和釋放
    • 注意:不要返回局部變量的地址
    • 每調(diào)用一個函數(shù)就會給它分配一段連續(xù)的棧空間,等函數(shù)調(diào)用完畢后會自動回收這段??臻g
    • 自動分配和回收
  • 堆空間
    • 需要主動去申請和釋放
    • 由程序員分配釋放,當(dāng)程序結(jié)束時,由操作系統(tǒng)回收
    • 在C++中主要利用new在堆去開辟內(nèi)存, 利用delete釋放

堆空間

  • 在程序運行過程,為了能夠自由控制內(nèi)存的生命周期、大小,會經(jīng)常使用堆空間的內(nèi)存
  • 堆空間的申請 釋放
    • malloc \ free
    • new \ delete
    • new [] delete[]
  • 注意

    • 申請堆空間成功后,會返回那一段內(nèi)存空間的地址
    • 申請和釋放必須是 1 對 1 的關(guān)系,不然可能會存在內(nèi)存泄露
  • 現(xiàn)在的很多高級編程語言不需要開發(fā)人員去管理內(nèi)存(比如 Java ),屏蔽了很多內(nèi)存細節(jié),利弊同時存在

    • 利:提高開發(fā)效率,避免內(nèi)存使用不當(dāng)或泄露
    • 弊:不利于開發(fā)人員了解本質(zhì),永遠停留在 API 調(diào)用和表層語法糖,對性能優(yōu)化無從下手

堆空間初始化

int *p0 = (int*)malloc(sizeof(int)); //p1 未初始化
memset(p0, 0, sizeof(int*)); // 將p1的每一個字節(jié)都初始化為0
int *p1 = new int; //未初始化
int *p2 = new int(); //被初始化為0
int *p3 = new int(3); // 被初始化為5
int *p4 = new int[3]; // 數(shù)組元素未被初始化
int *p5 = new int[3](); // 3個數(shù)組元素被初始化為0
int *p6 = new int[3]{};  // 3個數(shù)組元素被初始化為0
int &p7 = new int[3]{5}; // 數(shù)組首元素被初始化為6,其他元素初始化為0

對象的內(nèi)存

  • 對象的內(nèi)存可以存在于 3 種地方

    • 全局區(qū)(數(shù)據(jù)段):全局變量
    • ??臻g:函數(shù)里面的局部變量
    • 堆空間:動態(tài)申請內(nèi)存( malloc 、 new 等)
    // 全局區(qū)
      Person person;
      int test() {
        // 棧空間
        Person person;
        // 堆空間
        Person * p = new Person;
        return 0;
      }
    

構(gòu)造函數(shù)(Constructor)

  • 構(gòu)造函數(shù)(也叫構(gòu)造器),在對象創(chuàng)建的時候自動調(diào)用,一般用于完成對象的初始化工作
  • 特點
    • 函數(shù)名與類同名,無返回值( void 都不能寫),可以有參數(shù),可以重載,可以有多個構(gòu)造函數(shù)
    • 一旦自定義了構(gòu)造函數(shù),必須用其中一個自定義的構(gòu)造函數(shù)來初始化對象
  • 注意
    • 通過 malloc 分配的對象不會調(diào)用構(gòu)造函數(shù)
  • 一個廣為流傳的、很多教程 書籍都推崇的錯誤結(jié)論
    • 默認情況下,編譯器會為每一個類生成空的無參的構(gòu)造函數(shù)
    • 正確理解:在某些特定的情況下,編譯器才會為類生成空的無參的構(gòu)造函數(shù)

默認情況下,成員變量的初始化

  struct Person {
         int m_age;

         Person() {
             /// 將所有成員變量清0
             memset(this, 0, sizeof(Person));
             cout << "Person()" << endl;
         }

         Person(int age) {
             m_age = age;
             cout << "Person(age)" << endl;
         }
     };

   // 全局區(qū):成員變量初始化為0
   Person g_person0; // Person()
   Person g_person1(); // 不會調(diào)動構(gòu)造方法,僅僅是函數(shù)的聲明
   Person g_person2(10); // Person(int)

   void test() {
      // 棧空間:沒有初始化成員變量
      Person person0;// Person()
      Person person1(); // 不會調(diào)動構(gòu)造方法,僅僅是函數(shù)的聲明
      Person person2(30); // Person(int)

      // 堆空間:沒有初始化成員變量
      Person * p0 = new Person;// Person()
      Person * p1 = new Person();// Person()
      Person * p2 = new Person(30); // Person(int)

      Person person1; //棧空間(成員變量不會不被初始化)

      // 堆空間
      Person * p2 = new Person; // 成員變量不會被初始化
      Person * p3 = new Person(); // 成員變量初始化為0
      Person * p4 = new Person[3]; /// 成員變量不會被初始化
      Person * p5 = new Person[3]();//3個person對象的成員變量都初始化為0
      Person * p6 = new Person[3]{}; // 3個person對象的成員變量都初始化為0
   }
  • 如果自定義了構(gòu)造函數(shù),除了全局區(qū),其他內(nèi)存空間的成員變量默認都不會被初始化,需要開發(fā)人員手動初始化

構(gòu)造函數(shù)的分類及調(diào)用

  • 按參數(shù)分類:有參構(gòu)造和無參構(gòu)造
  • 按類型分為: 普通構(gòu)造和拷貝構(gòu)造

三種調(diào)用方式

  • 括號法
    Person p = p(10);
    
  • 顯示法
    Person p = Person(10);
    
  • 隱式轉(zhuǎn)換法
    Person p = 10; // 相當(dāng)于寫了 Person p = Person(10)
    

構(gòu)造函數(shù)調(diào)用規(guī)則

  • 默認情況下(有需要處理事的時候),c++編譯器至少給一個類添加3個函數(shù)
    • 默認構(gòu)造函數(shù)
    • 默認析構(gòu)函數(shù)
    • 默認拷貝函數(shù)
  • 如果用戶定義有參構(gòu)造函數(shù),c++不再提供默認無參構(gòu)造函數(shù),但是會提供默認的拷貝構(gòu)造
  • 如果用戶定義拷貝構(gòu)造函數(shù), c++不會再提供其他構(gòu)造函數(shù)
  • 當(dāng)其他類對象作為本類成員,構(gòu)造時候先構(gòu)造類對象,再構(gòu)造自身,析構(gòu)的順序與構(gòu)造相反

析構(gòu)函數(shù)(Destructor)

  • 析構(gòu)函數(shù)(也叫析構(gòu)器),在對象銷毀的時候自動調(diào)用,一般用于完成對象的清理工作
  • 特點
    • 函數(shù)名以 開頭,與類同名,無返回值( void 都不能寫),無參,不可以重載,有且只有一個析構(gòu)函數(shù)
  • 注意
    • 通過 malloc 分配的對象 free 的時候不會調(diào)用構(gòu)造函數(shù)
    • 構(gòu)造函數(shù)、析構(gòu)函數(shù)要聲明為 public ,才能被外界正常使用
obj_mng.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容