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

震驚!用了 std::optional 后,我再也沒(méi)有因?yàn)榭罩羔槺活I(lǐng)導(dǎo)罵過(guò)

開(kāi)發(fā)
今天我們就來(lái)聊聊 C++17 引入的一個(gè)神器——std::optional,它能讓你優(yōu)雅地處理"可能存在,也可能不存在"的值,從此和空指針說(shuō)拜拜!

大家好,我是小康。

哎,說(shuō)起空指針,估計(jì)每個(gè)程序員都有一把辛酸淚。昨天還在跑得好好的程序,今天突然就給你來(lái)個(gè)Segmentation fault,然后你就開(kāi)始了漫長(zhǎng)的debug之旅。是不是很熟悉這個(gè)場(chǎng)景?

今天我們就來(lái)聊聊 C++17 引入的一個(gè)神器——std::optional,它能讓你優(yōu)雅地處理"可能存在,也可能不存在"的值,從此和空指針說(shuō)拜拜!

一、為什么我們需要optional?

先來(lái)看個(gè)真實(shí)的場(chǎng)景。你寫(xiě)了個(gè)函數(shù),用來(lái)查找數(shù)組中的最大值:

#include <iostream>
#include <vector>

int findMax(const std::vector<int>& nums) {
    if (nums.empty()) {
        // 糟糕!空數(shù)組怎么辦?
        return ???; // 返回什么好呢?
    }
    
    int maxVal = nums[0];
    for (int num : nums) {
        if (num > maxVal) {
            maxVal = num;
        }
    }
    return maxVal;
}

看到了吧?當(dāng)數(shù)組為空時(shí),我們陷入了兩難:

  • 返回0?但萬(wàn)一數(shù)組里都是負(fù)數(shù)呢?
  • 返回-1?但萬(wàn)一-1就是數(shù)組中的有效值呢?
  • 返回nullptr?那就要返回指針,但指針指向哪里呢?局部變量?靜態(tài)變量?還要擔(dān)心內(nèi)存管理...
  • 拋異常?有點(diǎn)小題大做了...

你可能會(huì)說(shuō):"返回指針不行嗎?" 我們來(lái)看看:

int* findMax(const std::vector<int>& nums) {
    if (nums.empty()) {
        return nullptr;
    }
    
    staticint maxVal; // 用靜態(tài)變量?危險(xiǎn)!
    maxVal = nums[0];
    for (int num : nums) {
        if (num > maxVal) {
            maxVal = num;
        }
    }
    return &maxVal;
}

// 使用時(shí)的問(wèn)題:
int* result1 = findMax({1, 2, 3});
int* result2 = findMax({4, 5, 6});
std::cout << *result1 << std::endl; // 輸出6而不是3!被覆蓋了

用指針的話(huà),要么面臨生命周期問(wèn)題,要么要?jiǎng)討B(tài)分配內(nèi)存,麻煩得很!

這就是 optional 大顯身手的時(shí)候了!

二、optional閃亮登場(chǎng)

std::optional就像一個(gè)"盲盒",里面要么裝著你想要的值,要么啥也沒(méi)有。它完美解決了前面的所有問(wèn)題:

  • 不用糾結(jié)返回什么特殊值(0、-1都可能是有效值)
  • 不用擔(dān)心指針的生命周期和內(nèi)存管理
  • 語(yǔ)義超級(jí)清晰:有值就是有值,沒(méi)值就是沒(méi)值,絕不含糊

用起來(lái)特別優(yōu)雅:

#include <iostream>
#include <vector>
#include <optional>

std::optional<int> findMax(const std::vector<int>& nums) {
    if (nums.empty()) {
        return std::nullopt; // 優(yōu)雅地表示"沒(méi)有值"
    }
    
    int maxVal = nums[0];
    for (int num : nums) {
        if (num > maxVal) {
            maxVal = num;
        }
    }
    return maxVal; // 自動(dòng)包裝成optional
}

int main() {
    std::vector<int> nums1 = {3, 1, 4, 1, 5};
    std::vector<int> nums2 = {}; // 空數(shù)組
    
    auto result1 = findMax(nums1);
    auto result2 = findMax(nums2);
    
    if (result1.has_value()) {
        std::cout << "最大值是: " << result1.value() << std::endl;
    } else {
        std::cout << "數(shù)組為空,沒(méi)有最大值" << std::endl;
    }
    
    if (result2.has_value()) {
        std::cout << "最大值是: " << result2.value() << std::endl;
    } else {
        std::cout << "數(shù)組為空,沒(méi)有最大值" << std::endl;
    }
    
    return0;
}

運(yùn)行結(jié)果:

最大值是: 5
數(shù)組為空,沒(méi)有最大值

看到?jīng)]?代碼邏輯清晰,不會(huì)崩潰,還很好理解!

三、optional的各種玩法

1. 創(chuàng)建optional的幾種方式

#include <iostream>
#include <optional>
#include<vector>
#include <string>

int main() {
    // 方式1:直接賦值
    std::optional<int> opt1 = 42;
    
    // 方式2:使用make_optional
    auto opt2 = std::make_optional<int>(100);
    
    // 方式3:空的optional
    std::optional<std::string> opt3; // 默認(rèn)為空
    std::optional<std::string> opt4 = std::nullopt; // 顯式為空
    
    // 方式4:復(fù)雜類(lèi)型
    std::optional<std::vector<int>> opt5 = std::vector<int>{1, 2, 3};
    
    std::cout << "opt1有值嗎? " << (opt1.has_value() ? "是" : "否") << std::endl;
    std::cout << "opt3有值嗎? " << (opt3.has_value() ? "是" : "否") << std::endl;
    
    return 0;
}

輸出:

opt1有值嗎? 是
opt3有值嗎? 否

2. 安全地取值

#include <iostream>
#include <optional>

std::optional<int> divide(int a, int b) {
    if (b == 0) {
        returnstd::nullopt; // 除零?不存在的!
    }
    return a / b;
}

int main() {
    auto result1 = divide(10, 2);
    auto result2 = divide(10, 0);
    
    // 方法1:使用has_value()檢查
    if (result1.has_value()) {
        std::cout << "10 / 2 = " << result1.value() << std::endl;
    }
    
    // 方法2:使用value_or()提供默認(rèn)值
    std::cout << "10 / 0 = " << result2.value_or(-1) << " (默認(rèn)值)" << std::endl;
    
    // 方法3:直接用*操作符(要確保有值)
    if (result1) { // 可以直接當(dāng)bool用!
        std::cout << "用*操作符: " << *result1 << std::endl;
    }
    
    return 0;
}

輸出:

10 / 2 = 5
10 / 0 = -1 (默認(rèn)值)
用*操作符: 5

3. 鏈?zhǔn)讲僮鳌@個(gè)真的很酷!

C++23還引入了 transform 和 and_then 方法,讓你可以像處理管道一樣處理optional:

#include <iostream>
#include <optional>
#include <string>

// 模擬一個(gè)可能失敗的字符串轉(zhuǎn)數(shù)字函數(shù)
std::optional<int> stringToInt(const std::string& s) {
    try {
        return std::stoi(s);
    } catch (...) {
        return std::nullopt;
    }
}

// 平方函數(shù)
std::optional<int> square(int x) {
    if (x > 1000) { // 防止溢出
        return std::nullopt;
    }
    return x * x;
}

int main() {
    std::string input1 = "5";
    std::string input2 = "abc";
    std::string input3 = "50";
    
    // 傳統(tǒng)寫(xiě)法
    auto num1 = stringToInt(input1);
    if (num1.has_value()) {
        auto squared = square(*num1);
        if (squared.has_value()) {
            std::cout << "傳統(tǒng)寫(xiě)法: " << *squared << std::endl;
        }
    }
    
    // 現(xiàn)代寫(xiě)法(需要C++23,這里展示概念)
    // auto result = stringToInt(input1)
    //     .and_then([](int x) { return square(x); });
    
    // 我們用傳統(tǒng)方式模擬鏈?zhǔn)秸{(diào)用
    auto processString = [](conststd::string& s) -> std::optional<int> {
        auto num = stringToInt(s);
        if (!num.has_value()) return std::nullopt;
        return square(*num);
    };
    
    std::cout << "input1 \"5\" 的平方: " << processString(input1).value_or(-1) << std::endl;
    std::cout << "input2 \"abc\" 的平方: " << processString(input2).value_or(-1) << " (無(wú)效)" << std::endl;
    std::cout << "input3 \"50\" 的平方: " << processString(input3).value_or(-1) << std::endl;
    
    return 0;
}

輸出:

傳統(tǒng)寫(xiě)法: 25
input1 "5" 的平方: 25
input2 "abc" 的平方: -1 (無(wú)效)
input3 "50" 的平方: 2500

四、實(shí)戰(zhàn)應(yīng)用:配置文件解析器

來(lái)個(gè)實(shí)際點(diǎn)的例子。假設(shè)你要寫(xiě)個(gè)配置文件解析器,有些配置項(xiàng)可能存在,有些可能不存在:

#include <iostream>
#include <optional>
#include <unordered_map>
#include <string>

class ConfigParser {
private:
    std::unordered_map<std::string, std::string> config_;
    
public:
    ConfigParser() {
        // 模擬從文件讀取配置
        config_["host"] = "localhost";
        config_["port"] = "8080";
        // 注意:沒(méi)有"timeout" 和 database_host 配置
    }
    
    std::optional<std::string> getString(const std::string& key) {
        auto it = config_.find(key);
        if (it != config_.end()) {
            return it->second;
        }
        return std::nullopt;
    }
    
    std::optional<int> getInt(const std::string& key) {
        auto str = getString(key);
        if (!str.has_value()) {
            return std::nullopt;
        }
        
        try {
            return std::stoi(*str);
        } catch (...) {
            return std::nullopt;
        }
    }
};

int main() {
    ConfigParser config;
    
    // 獲取主機(jī)名
    auto host = config.getString("host");
    std::cout << "主機(jī): " << host.value_or("未配置") << std::endl;
    
    // 獲取端口號(hào)
    auto port = config.getInt("port");
    std::cout << "端口: " << port.value_or(80) << std::endl;
    
    // 獲取超時(shí)時(shí)間(不存在的配置)
    auto timeout = config.getInt("timeout");
    std::cout << "超時(shí): " << timeout.value_or(30) << "秒" << std::endl;
    
    // 實(shí)際使用中的判斷
    if (auto dbHost = config.getString("database_host")) {
        std::cout << "連接數(shù)據(jù)庫(kù): " << *dbHost << std::endl;
    } else {
        std::cout << "數(shù)據(jù)庫(kù)配置缺失,使用內(nèi)存存儲(chǔ)" << std::endl;
    }
    
    return 0;
}

輸出:

主機(jī): localhost
端口: 8080
超時(shí): 30秒
數(shù)據(jù)庫(kù)配置缺失,使用內(nèi)存存儲(chǔ)

五、optional VS 傳統(tǒng)方案

讓我們對(duì)比一下各種處理"可能不存在"值的方案:

#include <iostream>
#include <optional>
#include <vector>

// 傳統(tǒng)方案1:使用特殊值
int findFirstEven_Traditional(const std::vector<int>& nums) {
    for (int num : nums) {
        if (num % 2 == 0) {
            return num;
        }
    }
    return -1; // 特殊值表示"沒(méi)找到"
}

// 傳統(tǒng)方案2:使用指針
int* findFirstEven_Pointer(const std::vector<int>& nums) {
    staticint result; // 靜態(tài)變量,危險(xiǎn)!
    for (int num : nums) {
        if (num % 2 == 0) {
            result = num;
            return &result;
        }
    }
    return nullptr;
}

// 現(xiàn)代方案:使用optional
std::optional<int> findFirstEven_Optional(const std::vector<int>& nums) {
    for (int num : nums) {
        if (num % 2 == 0) {
            return num;
        }
    }
    return std::nullopt;
}

int main() {
    std::vector<int> nums1 = {1, 3, 4, 7, 8};
    std::vector<int> nums2 = {1, 3, 5, 7, 9};
    
    std::cout << "=== 測(cè)試有偶數(shù)的數(shù)組 ===" << std::endl;
    
    // 傳統(tǒng)方案1
    int result1 = findFirstEven_Traditional(nums1);
    if (result1 != -1) {
        std::cout << "傳統(tǒng)方案1: " << result1 << std::endl;
    }
    
    // 傳統(tǒng)方案2
    int* result2 = findFirstEven_Pointer(nums1);
    if (result2 != nullptr) {
        std::cout << "傳統(tǒng)方案2: " << *result2 << std::endl;
    }
    
    // 現(xiàn)代方案
    auto result3 = findFirstEven_Optional(nums1);
    if (result3.has_value()) {
        std::cout << "optional方案: " << *result3 << std::endl;
    }
    
    std::cout << "\n=== 測(cè)試全是奇數(shù)的數(shù)組 ===" << std::endl;
    
    std::cout << "傳統(tǒng)方案1: " << findFirstEven_Traditional(nums2) << " (可能和有效值混淆)" << std::endl;
    
    int* ptr_result = findFirstEven_Pointer(nums2);
    std::cout << "傳統(tǒng)方案2: " << (ptr_result ? "有值" : "無(wú)值") << std::endl;
    
    auto opt_result = findFirstEven_Optional(nums2);
    std::cout << "optional方案: " << (opt_result.has_value() ? "有值" : "無(wú)值") << std::endl;
    
    return 0;
}

輸出:

=== 測(cè)試有偶數(shù)的數(shù)組 ===
傳統(tǒng)方案1: 4
傳統(tǒng)方案2: 4
optional方案: 4

=== 測(cè)試全是奇數(shù)的數(shù)組 ===
傳統(tǒng)方案1: -1 (可能和有效值混淆)
傳統(tǒng)方案2: 無(wú)值
optional方案: 無(wú)值

看出差別了吧?optional方案最清晰,不會(huì)產(chǎn)生歧義!

六、性能怎么樣?

你可能會(huì)擔(dān)心:用optional會(huì)不會(huì)影響性能?答案是:幾乎不會(huì)!

std::optional的實(shí)現(xiàn)非常高效,它通常就是一個(gè)聯(lián)合體加上一個(gè)bool標(biāo)志位,開(kāi)銷(xiāo)很小。而且相比傳統(tǒng)的指針檢查,它還能避免很多潛在的bug。

#include <optional>
#include <chrono>
#include <iostream>
#include <unordered_map>
#include <string>

class Config {
private:
    std::unordered_map<std::string, std::string> data;
public:
    Config() {
        // 模擬從文件加載100個(gè)配置項(xiàng)
        for (int i = 0; i < 100; ++i) {
            data["key" + std::to_string(i)] = "value" + std::to_string(i);
        }
    }
    
    // 傳統(tǒng)方式
    std::string* getTraditional(const std::string& key) {
        auto it = data.find(key);
        return (it != data.end()) ? &it->second : nullptr;
    }
    
    // optional方式
    std::optional<std::string> getOptional(const std::string& key) {
        auto it = data.find(key);
        return (it != data.end()) ? std::optional<std::string>(it->second) : std::nullopt;
    }
};

int main() {
    Config config;
    const int iterations = 100000;  // 模擬10萬(wàn)次配置查詢(xún)
    
    // 測(cè)試傳統(tǒng)方式
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto result = config.getTraditional("key42");
        if (result) {
            volatile auto x = result->length();  // 模擬使用配置
        }
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto traditional_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    // 測(cè)試optional方式
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto result = config.getOptional("key42");
        if (result) {
            volatile auto x = result->length();  // 模擬使用配置
        }
    }
    end = std::chrono::high_resolution_clock::now();
    auto optional_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "配置查詢(xún)測(cè)試(10萬(wàn)次):" << std::endl;
    std::cout << "傳統(tǒng)方式耗時(shí):" << traditional_time.count() << " 微秒" << std::endl;
    std::cout << "optional方式耗時(shí):" << optional_time.count() << " 微秒" << std::endl;
    std::cout << "性能差異:" << (double)optional_time.count() / traditional_time.count() << "倍" << std::endl;
    
    return 0;
}

在我的機(jī)器上運(yùn)行,兩種方式的性能差異微乎其微,但optional的代碼更安全、更清晰。

配置查詢(xún)測(cè)試(10萬(wàn)次):
傳統(tǒng)方式耗時(shí):7060 微秒
optional方式耗時(shí):8581 微秒
性能差異:1.21544倍

七、總結(jié):optional的優(yōu)勢(shì)

用了這么多例子,我們來(lái)總結(jié)一下 optional 的好處:

  • 類(lèi)型安全:編譯時(shí)就能發(fā)現(xiàn)潛在問(wèn)題
  • 語(yǔ)義清晰:一眼就能看出這個(gè)值可能不存在
  • 性能優(yōu)秀:幾乎零開(kāi)銷(xiāo)的抽象
  • 易于使用:API設(shè)計(jì)得很人性化
  • 現(xiàn)代化:符合現(xiàn)代C++的設(shè)計(jì)理念

八、小貼士

最后給大家?guī)讉€(gè)使用 optional 的小技巧:

  • 區(qū)分錯(cuò)誤和空值:如果"沒(méi)有值"表示錯(cuò)誤情況,用異常;如果是正常情況,用optional
  • 善用value_or方法:它能讓你的代碼更簡(jiǎn)潔
  • 結(jié)合auto:讓編譯器推導(dǎo)類(lèi)型,代碼更干凈
  • 鏈?zhǔn)秸{(diào)用:C++23的 transform 和 and_then 很強(qiáng)大,值得學(xué)習(xí)

好了,關(guān)于 std::optional 就講到這里。從此以后,遇到"可能存在,可能不存在"的值,你就知道該用什么工具了。告別空指針崩潰,擁抱優(yōu)雅代碼!

記住,編程不只是讓程序跑起來(lái),更要讓程序跑得優(yōu)雅、安全、可維護(hù)。std::optional就是這樣一個(gè)讓你的C++代碼更優(yōu)雅的利器。

快去試試吧,你會(huì)愛(ài)上它的!

責(zé)任編輯:趙寧寧 來(lái)源: 跟著小康學(xué)編程
相關(guān)推薦

2015-09-09 10:50:32

模擬駕駛

2024-08-12 08:28:35

2019-02-27 15:15:41

汽車(chē)保險(xiǎn)自動(dòng)駕駛數(shù)據(jù)分析

2023-10-08 11:09:22

Optional空指針

2020-12-18 08:28:13

Redis數(shù)據(jù)數(shù)據(jù)庫(kù)

2024-12-06 10:12:20

2022-01-23 08:04:29

Windows 10微軟

2020-03-12 07:55:50

訪(fǎng)問(wèn)量飆升DDoS

2023-08-04 08:52:52

Optional消滅空指針

2024-02-01 12:09:17

Optional容器null

2024-02-28 09:03:20

Optional空指針Java

2023-03-27 07:39:07

內(nèi)存溢出優(yōu)化

2023-05-14 22:25:33

內(nèi)存CPU

2021-10-25 23:12:06

iOS應(yīng)用系統(tǒng)

2024-10-15 15:58:11

2020-08-26 07:37:25

Nacos微服務(wù)SpringBoot

2021-03-26 15:18:11

代碼工具Mockoon

2020-07-01 09:07:52

SQL索引語(yǔ)句

2021-02-07 22:20:30

5G網(wǎng)絡(luò)工具

2013-05-10 09:40:16

移動(dòng)Nokia移動(dòng)市場(chǎng)
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 欧美aⅴ在线观看 | 国产视频一区二区在线观看 | 欧美成人精品激情在线观看 | 久久久久国产精品www | 偷牌自拍 | 精品久久久久久一区二区 | 免费亚洲网站 | 日韩视频91| 亚洲三级在线观看 | 久久国产精品久久久久久 | 欧美一级在线免费 | 久久精品视频一区二区三区 | 福利视频1000 | 国产日韩欧美中文 | 免费艹逼视频 | 国产精品美女在线观看 | 国产成人高清 | 成人国产免费视频 | 亚洲国产视频一区 | 国产98在线 | 免费, | 精品视频一区二区三区在线观看 | 岛国视频| 九色在线 | 在线观看视频亚洲 | 午夜视频免费在线 | 国产精品美女久久久久aⅴ国产馆 | 天天干夜夜操视频 | 久久久一区二区三区 | 欧美性吧| 成人深夜小视频 | 精品国产欧美在线 | 成年视频在线观看福利资源 | 天天干天天插 | 亚洲黄色一级毛片 | 国产精品成人一区二区三区 | 国产精品久久久久久久久久久久 | 国产午夜三级一区二区三 | 久久精品91久久久久久再现 | 国产精品高潮呻吟久久aⅴ码 | 欧美精品三区 | 亚洲国产aⅴ精品一区二区 免费观看av |