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

cin cout

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

函數(shù)重載

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

extern “C”

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

  void func(int a) {
    cout << "func(int a) " << a << endl;
  }
}
  • 如果函數(shù)同時(shí)有聲明和實(shí)現(xiàn), 要讓函數(shù)聲明被 " extern " 修飾 ,函數(shù)實(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ā)時(shí),可能會(huì)經(jīng)常出現(xiàn)以下操作, C++ 在調(diào)用 C 語言 API 時(shí),需要使用 extern " 修飾 C 語言的函數(shù)聲明

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

    extern-c1.png

volidate 關(guān)鍵字

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

默認(rèn)參數(shù)

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

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

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

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

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

引用(Reference)

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

const

  • const 是常量的意思,被其修飾的變量不可修改
    • 如果修飾的是類、結(jié)構(gòu)體(的指針),其成員也不可以更改
    • 以下 5 個(gè)指針分別是什么含義?
      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 必須寫在 符號(hào)的左邊,才能算是常引用
  • const 引用的特點(diǎn)

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

數(shù)組的引用

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

引用的本質(zhì)

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

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

    • struct 和 class 的區(qū)別
      • struct 的默認(rèn)成員權(quán)限是 public
      • class 的默認(rèn)成員權(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對(duì)象
    Person person;
    person.m_age = 20;
    person.run();
    
    // 通過指針訪問person對(duì)象
    Person *p = &person;
    p->m_age = 30;
    p->run();
    
  • 上面代碼中 person 對(duì)象、 pPerson 指針的內(nèi)存都是在函數(shù)的棧空間,自動(dòng)分配和回收的, person對(duì)象地地址就是成員變量m_age的地址

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

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

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

  • 成員變量的類型相同時(shí),對(duì)象的內(nèi)存為所有成員變量的內(nèi)存之和
  • 類型不同時(shí),采用內(nèi)存對(duì)齊方式計(jì)算
  • 空對(duì)象占用的內(nèi)存空間為:1:c++編譯器會(huì)給每個(gè)空對(duì)象分配一個(gè)字節(jié)空間,是為了區(qū)分空對(duì)象占用內(nèi)存的位置
  • 每個(gè)空對(duì)象也應(yīng)該有一個(gè)獨(dú)一無二的內(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對(duì)象對(duì)象的第一個(gè)地址
pPerson->m_id = 40; // 相當(dāng)于是person對(duì)象的第一個(gè)地址賦值,給m_age賦值
pPerson->m_age = 50;

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

this

  • this 是指向當(dāng)前對(duì)象的指針
  • 對(duì)象在調(diào)用成員函數(shù)的時(shí)候,會(huì)自動(dòng)傳入當(dāng)前對(duì)象的內(nèi)存地址
  • 指向被調(diào)用的成員函數(shù)/變量所屬的對(duì)象
  • 當(dāng)形參和成員變量同名時(shí)i,可用this指針區(qū)分
  • 在類的非靜態(tài)成員函數(shù)中返回對(duì)象本身,可使用 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
  • 每個(gè)應(yīng)用都有自己獨(dú)立的內(nèi)存空間,其內(nèi)存空間一般都有以下幾大區(qū)域
    • 代碼段(代碼區(qū))
      • 用于存放代碼
    • 數(shù)據(jù)段(全局區(qū))
      • 用于存放全局變量等
      • 靜態(tài)變量
      • 存放常量(符串常量,const修飾的全局變量)
  • ??臻g
    • 存放局部變量,函數(shù)參數(shù)
    • 棧區(qū)的數(shù)據(jù)有編譯器管理開辟和釋放
    • 注意:不要返回局部變量的地址
    • 每調(diào)用一個(gè)函數(shù)就會(huì)給它分配一段連續(xù)的??臻g,等函數(shù)調(diào)用完畢后會(huì)自動(dòng)回收這段??臻g
    • 自動(dòng)分配和回收
  • 堆空間
    • 需要主動(dòng)去申請(qǐng)和釋放
    • 由程序員分配釋放,當(dāng)程序結(jié)束時(shí),由操作系統(tǒng)回收
    • 在C++中主要利用new在堆去開辟內(nèi)存, 利用delete釋放

堆空間

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

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

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

堆空間初始化

int *p0 = (int*)malloc(sizeof(int)); //p1 未初始化
memset(p0, 0, sizeof(int*)); // 將p1的每一個(gè)字節(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個(gè)數(shù)組元素被初始化為0
int *p6 = new int[3]{};  // 3個(gè)數(shù)組元素被初始化為0
int &p7 = new int[3]{5}; // 數(shù)組首元素被初始化為6,其他元素初始化為0

對(duì)象的內(nèi)存

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

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

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

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

默認(rèn)情況下,成員變量的初始化

  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(); // 不會(huì)調(diào)動(dòng)構(gòu)造方法,僅僅是函數(shù)的聲明
   Person g_person2(10); // Person(int)

   void test() {
      // ??臻g:沒有初始化成員變量
      Person person0;// Person()
      Person person1(); // 不會(huì)調(diào)動(dòng)構(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; //??臻g(成員變量不會(huì)不被初始化)

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

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

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

三種調(diào)用方式

  • 括號(hà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ī)則

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

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

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