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

五分鐘掌握C++參數(shù)傳遞精髓,從此告別內(nèi)存泄漏

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

嘿,C++程序員們! 

你是否曾經(jīng)對著代碼發(fā)呆,思考這些問題:

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

別擔(dān)心!本指南將為你揭開C++參數(shù)傳遞的神秘面紗,帶你領(lǐng)略其中的優(yōu)雅與智慧。讓我們一起踏上這段奇妙的代碼之旅吧! 

本指南將幫助你:

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

準(zhǔn)備好了嗎?讓我們開始探索C++參數(shù)傳遞的藝術(shù)吧! 

輸入?yún)?shù)的傳遞方式 - 該值傳值還是傳引用? 

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

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

為什么呢?因?yàn)椋?/p>

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

來看個例子:

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

// 處理坐標(biāo)
void movePoint(int x, int y);                // ?? 坐標(biāo)是簡單的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就是個數(shù)字,傳值
void processId(const int& id);               // ?? 完全沒必要用引用

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

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

為什么要這樣做呢? 

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

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

來看個有趣的例子:

// 糟糕: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像個實(shí)習(xí)生,明確表示要被指導(dǎo)和修改
void parse_name(string& str);

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

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

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

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

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

需要當(dāng)心的陷阱

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

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

如何避免掉進(jìn)這些坑? 

幸運(yùn)的是,編譯器是我們的好朋友,會幫我們把關(guān):

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

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

小貼士

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

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

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

為什么要這么演? 

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

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

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

來看個

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

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

但是要注意!

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

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

檢查清單

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

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

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

返回值 vs 輸出參數(shù)之戰(zhàn)

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

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

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

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

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

什么時候該用輸出參數(shù)? 

當(dāng)然也有一些特殊情況需要用輸出參數(shù):

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

返回值優(yōu)化的小魔法

現(xiàn)代C++編譯器會幫你優(yōu)化返回值,所以不用太擔(dān)心性能:

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

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

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

返回多個值時,用結(jié)構(gòu)體來裝! 

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

有些人可能會這樣寫:

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

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

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

// 優(yōu)雅的寫法 ?
struct Stats {
    double mean;    // 平均值
    double std_dev; // 標(biāo)準(zhǔn)差
};

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

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

C++17的結(jié)構(gòu)化綁定讓使用更加優(yōu)雅:

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

特殊情況:輸入輸出參數(shù)

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

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

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

這種情況下使用引用參數(shù)是合理的,因?yàn)?

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

返回復(fù)雜數(shù)據(jù)的最佳實(shí)踐

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

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

// 使用示例
Location get_current_location() {
    // ... 獲取GPS數(shù)據(jù) ...
    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中返回查詢結(jié)果:"這個查詢返回了-1、false和'error occurred'"。讓人一頭霧水! 

換成結(jié)構(gòu)體就清晰多了:

struct QueryResult {
    int status_code;     // HTTP狀態(tài)碼
    bool has_data;       // 是否找到數(shù)據(jù)
    string error_msg;    // 錯誤信息
};

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

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

性能優(yōu)化小技巧

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

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

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

最后的建議

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

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

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

兩個"主角"的性格特點(diǎn):

(1) 指針君(T*):

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

(2) 引用妹(T&):

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

如何選擇約會對象?

當(dāng)你在寫代碼時,遇到這樣的場景:

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

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

愛情忠告

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

特別提醒

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

想要安全感滿滿?

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

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

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

C++參數(shù)傳遞總結(jié)

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

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

責(zé)任編輯:趙寧寧 來源: everystep
相關(guān)推薦

2021-06-07 09:51:22

原型模式序列化

2009-11-17 14:50:50

Oracle調(diào)優(yōu)

2024-12-25 12:00:00

C++解包代碼

2025-01-24 08:38:47

2025-01-08 13:00:00

指針內(nèi)存泄漏C++

2025-01-21 07:39:04

Linux堆內(nèi)存Golang

2009-11-05 10:55:22

Visual Stud

2021-01-11 09:33:37

Maven數(shù)目項(xiàng)目

2017-01-10 09:07:53

tcpdumpGET請求

2020-03-03 19:59:38

主板無線網(wǎng)卡

2021-01-13 09:23:23

優(yōu)先隊(duì)列React二叉堆

2018-01-08 16:19:04

微信程序輪播圖

2021-06-06 13:08:22

C#特性Attribute

2009-11-16 10:53:30

Oracle Hint

2024-12-11 07:00:00

面向?qū)ο?/a>代碼

2025-03-13 06:22:59

2022-08-04 13:27:35

Pythonopenpyxl

2021-10-20 06:58:10

工具低代碼無代碼

2020-06-16 08:47:53

磁盤

2017-04-25 12:07:51

AndroidWebViewjs
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號