简介
术语
前面已经使用并编写了一些 C++ 函数,现在来准确了解相关术语。下面的代码标注了常见名称,便于查阅。由于不影响语义的空白会被忽略,示例特意调整格式,让各部分分别占一行。
// Function declaration:
bool // Return type
admin_detected(string user, string password) // Type signature
; // Don't forget the ';' for the declaration
// Function definition:
bool // Return type
admin_detected // Function name
(string user, string password) // Parameter list
{ return user == "admin" && password == "1234"; } // Function body
进阶:声明与定义 声明相当于通知编译器:存在一个具有指定名称、返回类型和参数列表的函数。只有声明而没有定义,程序仍无法正常链接运行。若函数定义已出现在使用之前,不必再另写声明;若先使用后定义,则需要提前声明。声明也可处理相互调用,并将接口与实现分离。
const 限定符
有时需要保证某些值在初始化后不再改变。C++ 使用 const 关键字限定这样的常量。
const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
注意 你可能经常看到使用全大写加下划线命名的常量。若项目没有其他约定,建议将这种命名风格保留给宏。
尝试修改已经初始化的常量会产生编译错误。这既能避免意外修改,也可能为编译器提供优化机会。知道代码中的某些值不会改变,也有助于理解程序。
函数参数也可以使用 const。
string guess_number(const int& secret, const int& guess) {
if (secret < guess) return "lower.";
if (secret > guess) return "higher.";
return "exact!";
}
将对象以 const 引用传入函数,可以保证函数不通过该引用修改对象。长字符串等复制成本较高的对象经常这样传递。const 的另一种用途是标记不修改对象状态的成员函数。
class Stubborn {
public:
Stubborn(string reply) {
response = reply;
}
string answer(const string& question) const {
if (question.length() == 0) { return ""; }
return response;
}
private:
string response{};
};
Stubborn 的成员函数 answer 使用 const string& 参数,避免复制传入的原对象。
函数重载
参数列表不同时,多个函数可以使用相同名称,这称为函数重载。通常在这些函数执行相似任务时使用。
判断重载时,函数名称和参数类型等构成签名;改变这些部分可以形成新的重载。返回类型本身不能用于区分重载。
play_sound 示例提供了六种重载,以适应不同场景:
// different argument types:
void play_sound(char note); // C, D, E, ..., B
void play_sound(string solfege); // do, re, mi, ..., ti
void play_sound(int jianpu); // 1, 2, 3, ..., 7
// different number of arguments:
void play_sound(string solfege, double duration);
// different qualifiers:
void play_sound(vector<string>& solfege);
void play_sound(const vector<string>& solfege);
进阶:函数签名 函数签名涉及名称、参数数量、参数类型及相关限定,不包含参数名。返回类型不能单独区分重载;若两个函数仅返回类型不同,会产生编译错误,因为调用时无法据此决定应使用哪一个。
默认参数
有些函数的参数列表很长,而许多调用对大多数参数都使用相同值。默认参数可以避免反复填写这些值。
void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon");
record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccaba", sire "Poseidon"
record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen"); // sire will be "Poseidon"
读者通常先看到声明,因此默认值最好写在声明处。如果某个参数提供默认值,其右侧参数也都必须提供默认值。有时可以用带默认参数的少量函数替代复杂的多组重载,改善可维护性。
任务说明
继续编写与千层面准备和烹饪有关的代码,按照最喜欢的食谱制作美味佳肴。
你需要完成五个任务,涉及制作时间及食材准备。
1. 估算准备时间
下一次制作千层面时,你希望预留足够时间,从容享受烹饪。你已规划好各层食材,现在需要据此估算准备时长。
实现 preparationTime,接收表示各层的字符串动态数组,以及 int 类型的每层平均准备分钟数,返回 int 类型的预计总准备时间。若没有传入平均时间,默认使用 2。
std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12
2. 计算面片与酱汁用量
除了安排时间,还要确保面片和酱汁足够。每层面片需要 50 克面片,每层酱汁需要 0.2 升酱汁。
定义 quantities,接收表示各层的字符串动态数组,计算所需食材。返回已经为你定义好的 amount 结构体,其中 noodles 是表示面片克数的 int,sauce 是表示酱汁升数的 double。
struct amount { int noodles; double sauce; };
amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4
3. 添加秘密食材
不久前你在朋友家吃到了特别美味的千层面。朋友发来食材清单,并告诉你最后一项就是使味道与众不同的秘密。你想把它加入自己的食谱。
编写 addSecretIngredient,接收两个字符串动态数组。第一个参数是自己食材清单的引用,第二个是朋友的清单。自己清单的最后一项始终为 "?",把它替换成朋友清单的最后一项。注意:函数不返回任何值,应直接修改自己的清单。朋友的清单可用引用传递以避免复制,但不能修改,应使用 const 引用保证这一点。
std::vector<std::string> friendsList{"noodles", "sauce", "mozzarella", "kampot pepper"};
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
addSecretIngredient(myList, friendsList);
// myList => {"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
4. 按份数调整配方
食谱原有用量只够两份。下次要为更多人做饭,因此需要计算其他份数所需的用量。
实现接收两个参数的 scaleRecipe:
- 一个
double动态数组,表示两份食物所需的各项用量。 - 希望制作的份数。
返回一个新的动态数组,包含目标份数所需的用量。你仍要保留原食谱,因此函数不能修改传入的 quantities。
std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }
5. 揭开家族秘方
你想起童年听过的故事:家族中的一位长辈保存着最深的烹饪秘密。据说她掌握非凡风味的秘诀,但方式独特,从不给整张清单或用量,只会一次透露一种食材。
你怀着期待又忐忑的心情联系她。果然,她用神秘的方式给出了一种秘密食材。
你希望把这份特别的馈赠加入食谱。为 addSecretIngredient 实现另一种重载,接收自己的食材动态数组引用,以及一个表示长辈所给食材的字符串。
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}
祝你好运,勇敢的厨师!