自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深度揭秘:為什么C++要發(fā)明引用?指針不夠用嗎?

開發(fā)
C++中引用(Reference)的引入不僅讓代碼更加優(yōu)雅,還解決了許多實際問題。今天,讓我們一起回顧這段充滿智慧與趣味的歷史。

在C++的發(fā)展歷程中,引用(Reference)的引入是一個重要的里程碑。它不僅讓代碼更加優(yōu)雅,還解決了許多實際問題。今天,讓我們一起回顧這段充滿智慧與趣味的歷史。

最初的困擾

在1979年的某一天,Bjarne Stroustrup正坐在貝爾實驗室的辦公室里,盯著滿屏幕的星號和&符號,頭都大了三圈... 

// 看看這個讓人頭疼的代碼...
class Matrix {
    double data[1000][1000];  // 100萬個數字!
public:
    // 嘗試用指針重載減法運算符 - 這是個糟糕的主意!
    Matrix* operator-(Matrix* other) {
        // ... 實現矩陣減法 ...
        return this;
    }
};

// 方法1:傳值 - 復制整個對象(超慢!)
void process1(Matrix m) {     // ?? 慢得像蝸牛爬
    // 每次調用都復制100萬個數字...天??!
}

// 方法2:使用指針 - 快是快,但是...
void process2(Matrix* m) {    // ?? 到處都是幽靈一樣的星號
    m->data[0][0] = 1.0;     // 為什么要寫 -> ?好煩!
    (*m).data[1][0] = 2.0;   // 這括號是什么鬼?!
    if (!m) return;          // 別忘了檢查空指針...
}

int main() {
    Matrix huge;
    Matrix* pa = &huge;
    Matrix* pb = &huge;
    
    // ?? 噢不!這行代碼會導致災難!
    Matrix* result = pa - pb;  
    // 編譯器:這是指針算術!我要計算兩個指針之間的距離!
    // 程序員:不不不!我是想做矩陣減法啊!
    
    process1(huge);          // 復制100萬個數字 ??
    
    process2(&huge);         // 記得加&...否則編譯錯誤 ??
    
    // 交換兩個矩陣?更痛苦!
    Matrix m1, m2;
    swapMatrix(&m1, &m2);    // 又是這討厭的&... 
}

"這代碼丑得我都不忍心看了!" 我抓狂地喊道。

問題在哪?讓我數數:

  • 傳值?復制100萬個數字,慢得像蝸牛爬 ??
  • 用指針?代碼里星號和箭頭滿天飛 ??
  • 忘記加&?編譯器就會對你大發(fā)雷霆 ??
  • 忘記檢查空指針?程序直接跟你說拜拜 ??
  • 那些煩人的括號和箭頭,看得人眼花繚亂 ????
  • 指針算術和運算符重載混在一起?完全是場災難!??

更糟糕的是,使用指針來重載運算符會導致無法解決的歧義:

  • 指針算術是語言的基本特性
  • 它的優(yōu)先級高于用戶定義的運算符重載
  • 編譯器看到指針運算時,總是優(yōu)先使用內置的指針算術規(guī)則

就在我快要放棄的時候...

靈光乍現!

Doug McIlroy路過辦公室,看到Stroustrup抱著頭苦惱的樣子,隨口說了一句:"為什么不用引用呢?"

就像被閃電擊中一樣!?? Stroustrup立刻明白了!

// 看看這清爽的代碼!
void process(Matrix& m) {     // 沒有星號!沒有箭頭!?
    m.data[0][0] = 1.0;      // 直接用點運算符,多優(yōu)雅!
}                            // 不用檢查空指針!

void swapMatrix(Matrix& a, Matrix& b) {  // 告別煩人的&符號
    Matrix temp = a;
    a = b;
    b = temp;
}

int main() {
    Matrix m1, m2;
    
    process(m1);        // 看!多干凈!不用加&
    swapMatrix(m1, m2); // 優(yōu)雅得像跳芭蕾!??
}

引用就像是給變量起了個別名,既保持了指針的效率,又避免了指針的各種煩惱:

  • 不用加&和*,代碼整潔干凈
  • 不用檢查空值,更加安全
  • 不用寫煩人的箭頭運算符
  • 不用擔心忘記解引用

"這簡直是...太美了!" 我擦了擦感動的淚水

最令人興奮的是,引用完美解決了指針無法處理的運算符重載問題:

class Matrix {
    double data[1000][1000];
public:
    // 使用引用重載運算符 - 優(yōu)雅又直觀!?
    Matrix& operator-(const Matrix& other) {
        for(int i = 0; i < 1000; i++)
            for(int j = 0; j < 1000; j++)
                data[i][j] -= other.data[i][j];
        return *this;
    }
};

int main() {
    Matrix m1, m2;
    Matrix result = m1 - m2;  // 完美!編譯器知道這是矩陣減法 ?
                             // 不會和指針算術混淆
    
    // 鏈式操作也沒問題
    Matrix m3;
    (m1 - m2) - m3;          // 返回引用,可以繼續(xù)運算 ??
}

為什么引用能解決這個問題?因為:

  • 引用不支持算術運算,避免了和指針算術的歧義
  • 返回引用可以實現鏈式操作,而且不會產生臨時對象
  • 代碼更直觀,就像在寫數學表達式一樣自然

(但是等等...這個看似完美的方案里還藏著一個小陷阱...你猜是什么?)

救火時刻

"等等,我們有個緊急情況!" 一個工程師沖進了我的辦公室。"代碼在燃燒!?? 用戶們都在抱怨引用的問題!"

原來,我們的引用設計太過嚴格了,連最簡單的代碼都無法編譯:

void print(int& x) { cout << x << endl; }

int main() {
    print(42);        // ?? 轟!編譯器炸了
    double pi = 3.14;
    print(pi);        // ?? 又炸了!
}

在Release 2.0中,我們找到了完美的解決方案 - const引用!就像是給引用戴上了一副"只讀眼鏡" 

void print(const int& x) { cout << x << endl; }  // 加上const魔法盾

int main() {
    print(42);        // 完美!?
    double pi = 3.14;
    print(pi);        // 優(yōu)雅!會自動創(chuàng)建臨時int變量 ?
}

這個小小的const關鍵字,就像是消防員一樣,撲滅了代碼中的火焰 ?? 它解決了兩個關鍵問題:

  • 可以接受右值(如字面量42)
  • 可以接受不同類型(如double轉int)

為什么呢?因為:

  • const引用允許綁定到臨時對象
  • 編譯器會自動創(chuàng)建臨時變量進行類型轉換
  • 臨時對象的生命周期會延長到引用作用域結束

為什么普通引用不能綁定右值?

這是因為C++的一個重要設計原則:普通的非const引用不能綁定到右值,為什么呢?讓我們看個例子:

void modify(int& x) {
    x = 100;    // 修改引用指向的值
}

int main() {
    int a = 42;
    modify(a);     // ? 正確:a是左值,可以被修改
    modify(42);    // ? 錯誤:42是右值,無法被修改!
    
    int& ref = 42; // ? 錯誤:不能用右值初始化非const引用
}

想想看,如果允許這樣做會發(fā)生什么?

  • 42是一個臨時的右值
  • 如果允許引用綁定到它
  • 然后通過引用修改它的值
  • 但是...修改一個臨時值有什么意義呢???♂?

這就像是...

  • 試圖修改一個快遞單號
  • 或者想要改變數字"42"的值 這顯然是沒有意義的!

const引用來救場

而const引用不同,它承諾:"我保證不會修改這個值":

void print(const int& x) {
    // x = 100;    // ? 編譯錯誤:不能修改const引用
    cout << x << endl;  // ? 只讀訪問沒問題
}

int main() {
    print(42);        // ? 完全正確!
    
    // 編譯器會偷偷做這些事:
    // {
    //     int temp = 42;      // 1. 創(chuàng)建臨時變量
    //     const int& x = temp;// 2. 引用綁定到臨時變量
    //     print(x);           // 3. 使用這個引用
    // }                       // 4. 臨時變量在這里銷毀
    
    const int& ref = 42;  // ? 也正確!
    cout << ref << endl;  // 輸出42
}

所以:

  • 普通引用不能綁定右值 - 因為可能會修改一個臨時值,這沒有意義
  • const引用可以綁定右值 - 因為保證只讀,所以是安全的

這就像是:

  • 普通引用說:"我要改變你!" - 右值:不行!我是臨時的!
  • const引用說:"我只看不改" - 右值:那好吧,請進!

(這個設計真是絕妙!它既保證了類型安全,又提供了足夠的靈活性。)

最后的勝利

終于!在經歷了無數次的嘗試和改進后,我們得到了一個優(yōu)雅得讓人想哭的解決方案:

// 看看這清爽的代碼,像是剛洗完澡的企鵝 ??
void swap(int& a, int& b) {  
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 1, y = 2;
    swap(x, y);      // 就這樣?沒錯,就這樣!?
    
    // 再也不用寫這樣的代碼了:
    // swap(&x, &y);  // ?? 再見了,煩人的&符號!
}

看看這個美麗的代碼!沒有煩人的星號,沒有討厭的取地址符,就像是把所有的雜草都除掉了一樣!

(但是等等...你猜怎么著?這個看似完美的方案在遇到const時還有一個有趣的小故事...) 

教訓時刻

這段歷史告訴我們:

  • 有時候靈感來自于一句隨意的對話 
  • 新功能要謹慎,別太激進(比如那個臨時變量的坑) 
  • const是你的好朋友!

所以下次當你使用引用的時候,記得感謝那個在辦公室走廊里的靈光一閃!

PS: 這就是為什么我們現在能優(yōu)雅地寫代碼,而不用到處寫 & 和 * 了!謝謝你,引用!

責任編輯:趙寧寧 來源: everystep
相關推薦

2013-05-02 09:16:16

程序員

2021-08-08 08:17:45

事件響應日志網絡安全

2024-10-16 10:50:00

2016-11-25 15:03:33

FacebookWIFI

2021-03-15 23:11:12

內存虛擬化技術

2019-11-15 10:41:10

Vim分屏終端

2022-11-28 09:58:58

C++開發(fā)

2013-12-19 10:08:52

AWS服務器

2024-02-22 14:06:39

C++指針開發(fā)

2010-01-20 14:03:12

C++程序

2010-01-22 15:14:37

學習C++

2020-03-25 13:39:33

AI訓練支付寶3D

2021-03-03 11:04:51

流量手機.5G

2013-10-23 14:28:30

2017-03-23 11:24:26

Windows 10Windows系統(tǒng)盤

2018-11-22 14:34:01

局域網IP擴容

2021-08-12 23:15:04

手機內存小米

2020-11-12 07:47:18

程序員管理時間

2013-06-14 13:27:36

內存Linux交換分區(qū)

2024-07-25 12:33:45

點贊
收藏

51CTO技術棧公眾號