成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

五分鐘掌握C++參數傳遞精髓,從此告別內存泄漏

開發 后端
本指南將為你揭開C++參數傳遞的神秘面紗,帶你領略其中的優雅與智慧。讓我們一起踏上這段奇妙的代碼之旅吧!

嘿,C++程序員們! 

你是否曾經對著代碼發呆,思考這些問題:

  • 這個參數該傳值還是傳引用? 
  • 為什么我的程序這么慢,是參數傳遞的問題嗎? 
  • 移動語義到底是什么黑魔法? 
  • 返回多個值時該用tuple還是struct? 

別擔心!本指南將為你揭開C++參數傳遞的神秘面紗,帶你領略其中的優雅與智慧。讓我們一起踏上這段奇妙的代碼之旅吧! 

本指南將幫助你:

  • 理解何時使用值傳遞vs引用傳遞
  • 掌握const引用的最佳實踐
  • 學會使用移動語義優化性能
  • 正確處理輸出參數和返回值

準備好了嗎?讓我們開始探索C++參數傳遞的藝術吧! 

輸入參數的傳遞方式 - 該值傳值還是傳引用? 

這是一個經典的C++困擾 - 參數到底該怎么傳?來看看這條黃金法則:

  • 如果參數很"便宜"(比如int、指針這種小家伙)就傳值 
  • 如果參數"貴"(比如string這種大塊頭)就傳const引用 

為什么呢?因為:

  • 傳值的好處是簡單直接,不用擔心指針解引用的開銷
  • 傳const引用的好處是避免拷貝大對象的開銷

來看個例子:

// 處理用戶信息
void processUserInfo(const string& name);     // ?? name可能很長,用const引用
void processUserInfo(string name);            // ?? 每次都要拷貝name

// 處理坐標
void movePoint(int x, int y);                // ?? 坐標是簡單的int,直接傳值
void movePoint(const int& x, const int& y);  // ?? 引用反而增加了開銷

// 處理配置
void updateConfig(const vector<int>& config); // ?? vector可能很大,用const引用
void updateConfig(vector<int> config);        // ?? 拷貝整個vector開銷太大

// 處理ID
void processId(int id);                      // ?? ID就是個數字,傳值
void processId(const int& id);               // ?? 完全沒必要用引用

當然規則總有例外,比如你想要移動語義的時候,可以考慮用右值引用(&&)。但是不要過度優化,簡單明了才是王道! 

對于需要修改的參數,使用非const引用傳遞

為什么要這樣做呢? 

想象一下,你把一個參數傳給函數,但不知道它會不會被修改 - 這就像把鑰匙借給別人,卻不知道他會不會偷偷配一把新的! 

使用非const引用就像給參數貼上一個大大的標簽:"警告 這個參數會被修改哦!" - 代碼意圖一目了然。

來看個有趣的例子:

// 糟糕:result像個謎一樣,不知道是輸入還是輸出
void calculate_sum(int values[], int count, int* result); 

// 完美:sum像個告示牌,一看就知道會被修改
void calculate_sum(const int values[], int count, int& sum);

// 糟糕:str像個間諜,不知道會不會被暗中修改
void parse_name(char* str);

// 完美:str像個實習生,明確表示要被指導和修改
void parse_name(string& str);

有趣的例外 - 別被表象騙了! 

有些類型看起來人畜無害,實際上卻能神不知鬼不覺地修改原對象:

class Widget {
    vector<int> data;
public:
    void add(int x) { data.push_back(x); }
};

void process(shared_ptr<Widget> w) 
{
    w->add(42);  // 看似人畜無害的按值傳遞,實際是個"內鬼"!
}

void update_iterator(vector<int>::iterator it)
{
    *it = 100;  // 迭代器雖然是按值傳遞,但有"穿墻"特技!
}

需要當心的陷阱

引用參數就像一把雙刃劍 - 既能輸入也能輸出,用不好就容易傷到自己:

class Person {
    string name_;
    int age_;
public:
    // 糟糕:這簡直是"奪舍"!完全替換了原對象
    void update(Person& p) { 
        *this = p;  // 危險!像換了個人似的
    }
    
    // 完美:像個溫柔的美容師,只改變你想改的部分
    void set_name(const string& new_name) { name_ = new_name; }
    void set_age(int new_age) { 
        if (new_age < 0) throw invalid_argument("年齡不能為負,你想穿越嗎?");
        age_ = new_age; 
    }
};

如何避免掉進這些坑? 

幸運的是,編譯器是我們的好朋友,會幫我們把關:

  • 如果一個非const引用參數沒被寫入,編譯器會提醒:"喂,你說要改它的,怎么放鴿子?" 
  • 如果一個非const引用參數被move了,編譯器也會警告:"這樣不太好吧,有點過分了..." 

所以記住這條黃金法則:當你想在函數里修改參數時,用非const引用準沒錯! 讓代碼既安全又清晰,何樂而不為呢? 

小貼士

想一想:如果你看到一個函數用了非const引用參數,你是不是立刻就能明白它要做什么?這就是好代碼的魅力所在! 

"移動"這出戲要這么演 - 參數傳遞的藝術

嘿,各位C++演員們! 今天我們來聊聊如何優雅地"移動"對象這出戲該怎么演

為什么要這么演? 

  • 想象一下,你要把一個大型字符串從一個函數傳遞到另一個函數:
  • 復制一遍(拷貝傳遞) ? - 需要分配新內存并復制所有字符

轉移所有權(移動傳遞) ? - 直接"偷走"原字符串的內存,超快!

這就是為什么我們要用移動語義 - 它讓數據傳遞更高效。

來看個

string make_greeting(string&& name)  // name說:"請盡管移動我!"
{
    string result = "Hello, ";
    result += std::move(name);      // 直接把name的內容"偷"過來
    return result;                  // result也會被移動返回,超高效!
}

// 使用示例
string name = "Alice";
string greeting = make_greeting(std::move(name)); 
// 現在name變空了,greeting里面有完整的問候語

但是要注意!

移動之后,原對象就會變成"空殼子",不能再使用它的值:

string str = "Hello";
process_string(std::move(str));  // str的內容被移走了
cout << str;          

檢查清單

  • 看到T&&參數,記得用std::move把它移走
  • 被移動后的對象不要再使用它的值
  • 如果對象后面還要用,就不要移動它

記住:移動是為了性能,但要負責任地使用。移動后的對象就像倒空的杯子,不要期待里面還有水! 

怎么樣,現在對移動語義是不是更清楚了呢? 讓我們一起寫出更高效的代碼吧! 

返回值 vs 輸出參數之戰

想象一下,你是一個快遞員,要把包裹送到客戶手中。你有兩個選擇:

  • 直接把包裹遞給客戶(返回值)
  • 讓客戶先給你一個空箱子,然后你把東西放進去(輸出參數)

顯然第一種方式更直觀對吧?客戶也不用準備空箱子,多方便~

// ?? 干凈利落的返回值方式
vector<Pizza*> find_pizzas(const vector<Pizza>&, Topping t); 

// ?? 麻煩的輸出參數方式
void find_pizzas(const vector<Pizza>&, vector<Pizza*>& out, Topping t);

什么時候該用輸出參數? 

當然也有一些特殊情況需要用輸出參數:

  • 如果你要搬運一臺特別重的鋼琴(昂貴的移動操作),最好讓客戶先準備好位置:
// 鋼琴太重了,還是用引用吧
void move_piano(Piano& destination); 
  • 如果你要在循環里反復使用同一個容器:
string message;
for(int i = 0; i < 100; i++) {
  // 重復使用message,避免創建新的
  append_to_message(message, i); 
}

返回值優化的小魔法

現代C++編譯器會幫你優化返回值,所以不用太擔心性能:

Matrix operator+(const Matrix& a, const Matrix& b) {
    Matrix result;
    // 編譯器:放心,我會幫你優化掉不必要的拷貝~
    return result; 
}

所以除非真的有特殊需求,就用簡單直觀的返回值方式吧!畢竟代碼寫出來是給人看的,要讓同事看得開心才對

記住:返回值就像遞快遞,輸出參數就像裝修房子 - 能直接遞過去為什么要讓人準備空房間呢? 

返回多個值時,用結構體來裝! 

在C++中,一個函數只能返回一個值。但有時我們確實需要返回多個值,該怎么辦呢? 

有些人可能會這樣寫:

// 糟糕的寫法 ??
void calculate_stats(const vector<int>& data, 
                    double& mean,    // 輸出參數:平均值
                    double& std_dev) // 輸出參數:標準差
{
    // 計算平均值
    mean = /* ... */;
    // 計算標準差
    std_dev = /* ... */;
}

// 使用時:
double avg, dev;
calculate_stats(numbers, avg, dev); // 不清楚哪個是輸入哪個是輸出

這種通過引用參數來"偷偷"返回多個值的方式,不僅可讀性差,而且容易讓人困惑。來看看更好的方式:

// 優雅的寫法 ?
struct Stats {
    double mean;    // 平均值
    double std_dev; // 標準差
};

Stats calculate_stats(const vector<int>& data)
{
    double mean = /* ... */;
    double std_dev = /* ... */;
    return {mean, std_dev}; // 清晰地返回所有結果
}

// 使用時:
auto stats = calculate_stats(numbers); // 一目了然!
cout << "平均值:" << stats.mean << "\n";

C++17的結構化綁定讓使用更加優雅:

auto [mean, std_dev] = calculate_stats(numbers);
cout << "平均值:" << mean << "\n";

特殊情況:輸入輸出參數

有時候使用引用參數也是合理的,比如處理文件輸入輸出:

// 這樣寫是合適的 ??
bool read_record(istream& in,      // 輸入輸出流
                Record& record)    // 輸出:讀取的記錄
{
    // 讀取記錄...
    return true; // 返回是否成功
}

// 使用示例:
Record r;
while (read_record(file, r)) {
    process(r);
}

這種情況下使用引用參數是合理的,因為:

  • istream本身就是設計為通過引用傳遞和修改的
  • Record可能很大,通過引用避免了復制開銷

返回復雜數據的最佳實踐

對于復雜的返回值,最好定義專門的類型:

// 表示地理位置
struct Location {
    double latitude;   // 緯度
    double longitude;  // 經度
    double altitude;   // 海拔(米)
    
    string toString() const {
        return fmt::format("({:.2f}, {:.2f}, {:.2f}m)", 
                          latitude, longitude, altitude);
    }
};

// 使用示例
Location get_current_location() {
    // ... 獲取GPS數據 ...
    return {37.7749, -122.4194, 0}; // 舊金山
}

不要使用pair/tuple,除非真的是臨時的、簡單的值對:

// 不好的寫法 ??
tuple<double,double,double> get_position() {
    return {37.7749, -122.4194, 0};
}

// 使用時不知道各個值的含義
auto [x, y, z] = get_position(); 

用tuple返回多個值就像在API中返回查詢結果:"這個查詢返回了-1、false和'error occurred'"。讓人一頭霧水! 

換成結構體就清晰多了:

struct QueryResult {
    int status_code;     // HTTP狀態碼
    bool has_data;       // 是否找到數據
    string error_msg;    // 錯誤信息
};

這樣一看,每個返回值的含義清清楚楚!不然調用者還得翻文檔:"等等,第一個是狀態碼還是錯誤碼?" 

記住:代碼是寫給人看的,要讓API調用者一眼就能看懂返回值的含義。除非你覺得讓別人猜謎很有趣... 

性能優化小技巧

對于大對象,使用移動語義避免復制:

struct HugeResult {
    vector<double> data;  // 可能很大的數據
    string metadata;
};

HugeResult process_data(const string& input) 
{
    HugeResult result;
    result.data = /* ... 大量計算 ... */;
    result.metadata = /* ... */;
    return result;  // 編譯器會自動使用移動語義
}

最后的建議

  • 優先使用結構體返回多個值
  • 結構體字段要有清晰的名字和注釋
  • 只在處理IO時使用引用參數
  • 考慮使用optional<T>表示可能失敗的操作

遵循這些原則,你的代碼會更加清晰、易維護! 

指針和引用的愛恨情仇 - 到底該用哪個?

兩個"主角"的性格特點:

(1) 指針君(T*):

  • 性格隨性,可以是 nullptr (就是說人家可以單身)
  • 經常說"我現在沒對象"

(2) 引用妹(T&):

  • 必須死心塌地跟一個對象綁定(非常專一)
  • 從出生就必須有對象,單身都不行!

如何選擇約會對象?

當你在寫代碼時,遇到這樣的場景:

// 相親角色: 顧客類
class Customer { /*...*/ };

// 場景1: 處理VIP折扣
class Order {
    // 錯誤寫法: 用引用妹,顯得太強勢了!
    void process_discount(Customer& vip) { /*...*/ }
    
    // 正確寫法: 用指針君,比較隨意自然
    void process_discount(Customer* maybe_vip) {
        if(maybe_vip) {  // 有VIP就打折
            give_discount();
        } else {         // 沒VIP就原價
            normal_price(); 
        }
    }
};

愛情忠告

  • 如果這段關系"可能不存在" → 選指針君(T*)
  • 如果是"一定要在一起" → 選引用妹(T&)

特別提醒

雖然可以強行給引用妹安排一個無效對象(Customer* p = nullptr; Customer& r = *p;),但這樣做會把關系搞得一團糟(未定義行為)。要尊重引用妹的專一本性!

想要安全感滿滿?

如果你想要指針君的瀟灑,又想要引用妹的可靠,可以試試這個:

void process_order(not_null<Customer*> customer) {
    // 這位指針君保證有對象,超級可靠!
    customer->validate();
}

記住:選擇權在你,但要尊重對方的性格特點。該放飛的時候就用指針,該專一的時候就用引用~

C++參數傳遞總結

  • 參數傳遞的選擇: 傳參就像坐車 - 小朋友(int、指針)直接抱著走就行,大胖子(string、vector)最好拉著手(用引用)省力氣。要是打算幫他減肥(修改值),就別戴手套(const)了,直接拉手更方便!??
  • 移動語義的使用: 移動就是個"搬家公司",不用復制家具直接整車搬走。但搬完后原來的房子就空了,可別指望還能在老房子找到沙發啊!所以除非真要"搬家",不然就別瞎折騰了。??
  • 返回值的處理: 返回東西就該像快遞小哥 - 直接送到家多痛快!非要客戶準備個箱子(輸出參數)多麻煩。要是要寄好幾樣東西,就打包成禮盒(結構體)吧,總比塞個麻袋(tuple)里讓人猜里面是啥強! ??
  • 指針與引用的選擇: 指針就像隨性的單身漢 - 今天有對象明天沒對象都行。引用可是專一的好姑娘 - 認定一個就得一直跟著。所以看你要找個什么樣的"對象"啦! ??

記住:代碼寫得越簡單,同事越喜歡你! ??

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

2021-06-07 09:51:22

原型模式序列化

2009-11-17 14:50:50

Oracle調優

2024-12-25 12:00:00

C++解包代碼

2025-01-24 08:38:47

2025-01-21 07:39:04

Linux堆內存Golang

2025-01-08 13:00:00

指針內存泄漏C++

2009-11-05 10:55:22

Visual Stud

2021-01-11 09:33:37

Maven數目項目

2017-01-10 09:07:53

tcpdumpGET請求

2020-03-03 19:59:38

主板無線網卡

2021-01-13 09:23:23

優先隊列React二叉堆

2018-01-08 16:19:04

微信程序輪播圖

2021-06-06 13:08:22

C#特性Attribute

2024-12-11 07:00:00

面向對象代碼

2025-03-13 06:22:59

2009-11-16 10:53:30

Oracle Hint

2020-06-16 08:47:53

磁盤

2021-10-20 06:58:10

工具低代碼無代碼

2022-08-04 13:27:35

Pythonopenpyxl

2017-04-25 12:07:51

AndroidWebViewjs
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九综合 | 日韩精品一区二区三区在线观看 | 亚洲精品4 | 欧美成人a∨高清免费观看 色999日韩 | 精品无码久久久久久国产 | 国产国拍亚洲精品av | 爱高潮www亚洲精品 中文字幕免费视频 | 国产精品视频久久久 | 伊人亚洲 | 欧美在线一区二区三区 | 精品九九 | 欧美精品在欧美一区二区少妇 | 成人精品一区二区 | 午夜网站视频 | 波多野结衣精品在线 | 日韩欧美1区2区 | 国产精品一区在线观看 | 国产午夜精品久久久 | 91视频免费视频 | 精品国产乱码久久久久久老虎 | 国产一区二区三区视频在线观看 | 亚洲av毛片 | 国产成人综合一区二区三区 | 青青草视频网 | 特级a欧美做爰片毛片 | 亚洲精选一区二区 | 日韩久久久久久 | 不卡一区二区三区四区 | www.一区二区三区 | 国产网站久久 | 久久久.com| 欧美一区二区三区在线视频 | 欧洲尺码日本国产精品 | 热re99久久精品国99热观看 | 亚洲三区视频 | www一级片 | 狼色网| 91爱爱·com| 亚洲激情综合 | 一级黄色大片 | 在线观看涩涩视频 |