简介
C++ 中的 auto 关键字
C++11 引入的 auto 用于声明由编译器推导类型的变量。编译器根据初始化表达式确定类型,有时能使代码更易读、易维护。
使用示例
下面使用 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.
后续课程中,会经常在 lambda 表达式、范围 for 循环和迭代器相关代码中见到它。
智能指针
智能指针提供自动内存管理,有助于避免原始指针常见的内存泄漏及部分悬空指针问题。它们封装原始指针,并在不再需要所管理资源时自动释放内存。
基本语法
智能指针通常是标准库中的类模板,最常用的是 std::unique_ptr 和 std::shared_ptr。
独占指针
std::unique_ptr 独占所管理对象,保证同一时刻只有一个这样的所有者。拥有资源的 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() 的优点
创建 unique_ptr 时,通常优先使用 std::make_unique(),而不是直接 new:
- 异常安全:对象构造抛出异常时,已分配的内存会被正确释放,避免泄漏。
- 清晰简洁:集中表达资源创建与所有权,不必重复书写原始指针管理代码;对象类型通过模板参数指定,构造参数可转发。
- 优化机会:这种明确的创建方式便于编译器分析与优化,具体效果取决于实现。
- 避免误用:手工管理同一原始指针时,可能提前删除资源,导致
unique_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[]>创建数组;该形式从 C++20 才提供。对象与数组的释放方式不同,处理数组时不能误用单对象删除方式。C++17 中可优先考虑std::vector,或使用符合标准的数组专用管理方式及必要的自定义删除器。务必确认编译器与标准库支持情况。
std::make_shared() 的优点
与 make_unique 类似,make_shared 有助于提高可读性和异常安全性。它通常把控制块与对象合并分配,减少分配次数与内存管理开销,降低资源管理出错的风险。对象类型由模板参数指定,构造参数得到转发,代码也更加清晰。使用它通常能使共享资源管理更安全、简洁。
使用建议
管理拥有所有权的资源时,优先考虑智能指针:独占资源使用 unique_ptr,确实需要共享所有权时使用 shared_ptr。原始指针适合表达非拥有关系,或对接旧代码。多数独占所有权场景使用 unique_ptr 即可,它不需要引用计数开销;shared_ptr 会增加开销和复杂度,应按实际共享需求使用。
任务说明
来到充满奇迹与魔法的特洛伊世界。这里许多人拥有异能,日常用它改变环境、影响动物或其他人。魔法还会凝聚为独一无二的神器,受到冒险者、工匠与学者追捧。
本练习需要为特洛伊人、他们拥有的神器及异能之间的作用编写模型。
你需要完成六个任务:首先创建人物,再处理神器与异能。
1. 为特洛伊世界创建人物
人物是模型的核心。他们可以拥有独一无二的神器,也可以觉醒异能。异能能够影响他人,因此还要记录人物是否受其他异能影响。
已经提供 artifact 和 power 结构体。实现 human 结构体或类,包含名为 possession、指向 artifact 的智能指针。同一时刻,每件神器只能属于一人。
human 还需要两个成员:保存自身异能的 own_power,以及保存所受异能的 influenced_by。二者都是指向 power 的智能指针。同一种异能可能由一人施展,同时影响其他人。
默认情况下,人物出生时没有神器、没有自身异能,也不受其他异能影响。
human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr
2. 创造神器
只有静态对象的模型有些无趣。现在创建独特神器,并交给指定人物。
定义 give_new_artifact,接收 human 引用和字符串,以字符串创建新的 artifact,并设置该人物的 possession。函数不返回值。
human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");
erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"
3. 让神器可以交换
特洛伊世界充满互动,人物应能通过交换所有物来交易。
编写 exchange_artifacts,接收两个神器智能指针并交换内容,不返回值。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"
4. 赋予人物异能
特洛伊最令人兴奋的是各种异能:有人能用意念熔化铁,有人在夜间能瞬间治愈伤口。
定义 manifest_power,接收人物引用与字符串,以字符串创建 power,并设置其 own_power。函数不返回值。
human eleven {};
manifest_power(eleven, "psychokinesis");
eleven.own_power->effect;
// "psychokinesis"
5. 使用异能
再强大的异能也需要实际使用。模型以人物为中心,因此要追踪异能对人物的影响。
编写返回 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"
6. 追踪异能强度
某些异能施加给多人后会减弱,或触发特殊效果,因此需要统计与每种异能关联的人数。
定义 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