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,因为它会带来额外开销和复杂性。
这个练习会带你走进特洛伊的世界。 那里的人们生活在奇妙与魔法之中。 特洛伊的许多人拥有_能力_,并在日常生活中频繁使用它们。 能力可以用来重塑世界,或者影响特洛伊的动物和其他人。 魔法还会凝结成_独特的神器_,它们深受冒险者、工匠和贤者的追捧。
在这个练习中,你将编写代码,为特洛伊的人类、他们拥有的神器和能力交互建立模型。
一共有六个任务。 第一个任务与创造人类有关,其余五个则关于处理能力和神器。
在你的特洛伊模型中,人类是最重要的部分。 你建模的人类应当能够拥有一件_独特的神器_。 他们还应当能够显现一种_能力_。 这些能力可能会影响其他人,所以你还想对人类是否受到其他能力的影响进行建模。
这里已经提供了 artifact 和 power 结构体的基本实现。
请实现一个 human 结构体(或类),它有一个指向名为 possession 的 artifact 成员变量的_智能指针_。
任何时候,每件神器都只能被一个人拥有。
human 还必须有两个额外的成员变量。
一个保存他们的 own_power,另一个是他们 influenced_by 的能力。
own_power 和 influenced_by 都是指向 powers 的_智能指针_。
每个 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,它不返回任何值,接收两个神器的智能指针,用来交换物品。
记住,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 新建一个 power 对象,并相应地设置该 human 的 own_power 指针。
这个函数不应返回任何值。
human eleven {};
manifest_power(eleven, "psychokinesis");
eleven.own_power->effect;
// "psychokinesis"
再强大的能力,不能用又有什么用呢。 你的模型重点关注人类,所以你想追踪能力的影响。
请编写一个 void 函数 use_power,它接收两个人类的引用。
第一个人是施法者,第二个代表目标。
目标的 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,它接收一个人,并以 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