auto 關鍵字在 C++ 中,auto關鍵字是 C++11 引入的強大功能,用來宣告型別會被推導出來的變數。
編譯器會根據初始值推導出變數的型別,讓程式碼更好讀、也更好維護。
看看下面這個用auto宣告變數的例子:
auto dragon_population{3}; // dragon_population is deduced as an integer
auto westeros{7.7777}; // westeros is deduced as a double
auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string
每一個例子裡,變數的型別都是從初始化時所用的值推導出來的。
auto關鍵字能減少明寫型別的冗長感,讓程式碼更精簡、更好讀。
const std::vector<std::string> pigeon_pie{"flour", "butter", "pigeon", "salt"};
auto purple_wedding_pie{pigeon_pie};
purple_wedding_pie.emplace_back("the strangler");
在這段迴圈中,auto把purple_wedding_pie的型別推導為std::vector<std::string>,不必再明寫一次型別。
auto關鍵字能與各種 C++ 結構搭配,是現代 C++ 程式設計中用途廣泛的工具。
auto& element{array[0]}; // reference to an element
const auto object{otherObject}; // const type version of otherObject's type
auto* ptr{&x}; // pointer to x with the same type as x, but as a pointer.
之後的單元裡,我們會經常看到auto關鍵字搭配 lambda 運算式、以範圍為基礎的for迴圈,以及迭代器。
智慧指標是現代 C++ 的功能,目的是提供自動記憶體管理,協助避免原生指標常見的記憶體洩漏和懸空指標。 它們像是原生指標的包裝,額外提供自動釋放記憶體等功能,在指標不再需要時自動把記憶體釋放掉。
智慧指標在 C++ 標準函式庫中通常以類別模板實作。
最常用的兩種智慧指標是std::unique_ptr和std::shared_ptr。
std::unique_ptr是一種獨佔物件所有權的智慧指標。
它確保在任何時刻,資源都只由一個std::unique_ptr物件所擁有。
當擁有資源的std::unique_ptr被銷毀或重置時,它會自動解構物件並釋放記憶體。
#include <memory>
// Declaring and defining a unique pointer
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");
// Unique pointers cannot be copied or assigned
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr
std::make_unique() 的優點建立std::unique_ptr時,建議使用std::make_unique(),而不是直接用new配置記憶體。
std::make_unique()有幾項優點:
std::make_unique()能保證例外安全。
如果在建構物件期間拋出例外,記憶體會自動被釋放,避免記憶體洩漏。std::make_unique()讓程式碼更清楚、更精簡。
它不必明寫要配置的型別,因為模板引數會自動推導出來。new手動配置記憶體,編譯器更有機會有效最佳化std::make_unique(),效能可能因此提升。std::unique_ptr時,有可能把底層資源刪除掉。
當std::unique_ptr在離開作用域時試圖刪除它,就會導致未定義行為。std::shared_ptr是一種智慧指標,允許讓多個std::shared_ptr物件共享同一個資源的所有權。
它會追蹤有多少個共享指標正在引用這個資源,只有在最後一個擁有該資源的共享指標離開作用域或被重置時,才會釋放記憶體。
// Declaring and defining a shared pointer to a dynamically allocated string
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");
// Creating more shared pointer that shares ownership
auto outer_planets_alliance = martian_congressional_republic;
auto united_nations = martian_congressional_republic;
在 C++17 及更早的版本中,並不直接支援透過std::make_shared<T[]>將std::shared_ptr用於陣列。
雖然可以用std::make_shared<T[]>配置陣列,但直接由它建立共享指標可能會導致未定義行為,因為單一物件和陣列在記憶體管理上有所不同。
建議改用std::vector或自訂刪除函式,才能有效以共享指標管理陣列。
在 C++17 中處理陣列配置與共享指標時,請務必確認與你的編譯器和標準函式庫實作相容。
std::make_shared() 的優點和std::make_unique()類似,std::make_shared()也帶來記憶體效率提升、例外安全和可讀性等好處。
它把控制區塊和受管理物件的記憶體配置合併成一次操作,提升效率並降低記憶體洩漏的風險。
此外,自動推導模板引數能簡化程式碼並提升可讀性。
在 C++ 中操作std::shared_ptr物件時,使用std::make_shared()能寫出更簡潔、更安全、更有效率的程式碼。
預設使用智慧指標:獨佔所有權用std::unique_ptr,共享所有權用std::shared_ptr。
原生指標則保留給不具所有權的引用,或與舊有程式碼介接時使用。
大多數情況下,std::unique_ptr就足以處理獨佔所有權,因為它提供輕量的記憶體管理,沒有引用計數的額外負擔。
std::shared_ptr則應謹慎少用,因為除非真的需要共享所有權,否則它會帶來額外負擔和複雜度。
這個練習會帶你進入特洛伊的世界。 這裡居民的生活充滿了驚奇與魔法。 特洛伊的許多人類擁有力量,並且在日常生活中頻繁使用。 力量可以用來重塑世界,或影響特洛伊的動物和其他人。 魔法也會顯現在獨特的器物上,這些器物深受冒險者、工匠和賢者追捧。
在這個練習中,你將撰寫程式碼來模擬特洛伊的人類、他們所擁有的器物,以及力量之間的互動。
你有 6 項任務。 第一項與建立人類有關,其餘 5 項則與處理力量和器物有關。
在你的特洛伊模型中,人類是最重要的部分。 你模型裡的人類應該要能擁有獨特的器物。 他們也應該要能顯現力量。 這些力量可能會影響其他人類,所以你也會想模擬人類是否受到其他力量影響。
你已經有artifact和power結構的基本實作。
請實作一個human結構(或類別),其中有一個名為possession的artifact成員變數,並以智慧指標指向它。
在任何時刻,每件器物只能由單一人類擁有。
human還必須有 2 個額外的成員變數。
一個用來存放own_power,另一個則存放他們influenced_by的力量。
own_power和influenced_by都是指向power的智慧指標。
每個power可能由單一人類擁有,但同時也能影響其他人類。
預設情況下,人類出生時不帶任何器物,既不擁有任何力量,也不受任何力量影響。
human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr
如果模型中的各個部分不能互動,就太無聊了。 你想創造獨特的器物,並把它們交給特定的人類。
定義函式give_new_artifact,它不會回傳任何東西,但接受一個human的參考和一個string。
它應該用這個string定義一個新的artifact物件,並據此設定該human的possession指標。
這個函式不應該回傳任何東西。
human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");
erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"
特洛伊的世界充滿了互動。 你希望人們能透過交換彼此的所有物來進行交易。
撰寫一個函式exchange_artifacts,它不會回傳任何東西,但接受 2 個器物智慧指標來交換物品。
請記住,你無法複製unique_ptr。
這也包括用在函式參數上的情況。
請改用unique_ptr的參考。
human uchiha{};
give_new_artifact(uchiha, "konoha headband");
human uzumaki{};
give_new_artifact(uzumaki, "forehead protector");
exchange_artifacts(uchiha.possession, uzumaki.possession);
uchiha.possession->name;
// "forehead protector"
uzumaki.possession->name;
// "konoha headband"
特洛伊最令人興奮的特色,就是人們可能擁有的特殊力量。 有些人能用意念熔煉鋼鐵,有些人則能在夜晚瞬間治癒所有傷口。
定義函式manifest_power,它不會回傳任何東西,但接受一個human的參考和一個string。
它應該用這個string定義一個新的powers物件,並據此設定該human的own_power指標。
這個函式不應該回傳任何東西。
human eleven {};
manifest_power(eleven, "psychokinesis");
eleven.own_power->effect;
// "psychokinesis"
再強大的力量,如果不能使用,又有什麼用呢。 你的模型聚焦於人類,所以你想追蹤力量的影響。
撰寫一個void函式use_power,它接受 2 個 human 的參考。
第一個是施術的人類,第二個則代表目標。
目標的influenced_by指標應該指向施術者的力量。
為了簡單起見,人類只能受到單一力量的影響。 即使施術者已經不存在,這股力量仍會持續作用。
human pamela_isley{};
manifest_power(pamela_isley, "control pheromones");
human count_vertigo{};
use_power(pamela_isley, count_vertigo);
count_vertigo.influenced_by->effect;
// "control pheromones"
某些力量在施加於多個人類時,會失去效力,或在你的模擬中觸發特定效果。 你想追蹤與每個力量連結的人數。
定義函式power_intensity,它接受一個 human,並以int回傳其力量的強度。
如果這個人沒有任何力量,回傳值應該是0。
否則,強度應該反映出施術者以及目前所有受影響的人。
human jean_grey{};
manifest_power(jean_grey, "uplifting personality");
human scott{};
human logan{};
human ororo{};
use_power(jean_grey, ororo);
use_power(jean_grey, logan);
use_power(jean_grey, scott);
power_intensity(jean_grey);
// 4