Functions in C++
C++中的函数
什么是函数
函数到底是什么?函数基本上是我们编写的代码块,旨在执行特定任务,后面我们讲到类时,这些块称为方法。但是当我说函数时,我明确地谈论的是不属于类的东西,我们拆分函数是为了解决重复代码。我们不想多次运行相同的代码,因为当然,如果我们除了复制和粘贴大量代码并最终导致这种巨大的混乱之外,这也意味着如果我们决定更改某些代码,我们必须在所有粘贴原始代码的地方更改它,这将是一个灾难,维护起来将是一场灾难。
所以我们能做的就是编写一个不错的函数来做我们想做的事情,然后如果需要,我们可以在我们的代码中多次调用它,你可以想到具有输入和输出的函数,尽管它们不一定需要我们可以为函数提供某些参数,并且函数可以为我们返回一个值。
因此我们编写一个函数将两个数字相乘。
首先我要在这里写的是称为返回值的东西,这是此函数将返回的类型,两个整型的数相乘,返回值也是整形。所以我们返回值也是int。函数的名字时Mutiply。函数包含两个参数a和b都是int类型。最后我们返回a*b。
int Multiply(int a, int b)
{
return a * b;
}
我们也可以不提供参数
int Multiply()
{
return 5 * 8;
}
我们也可以不返回任何值,通过使用void。void的意思是没有。
我们如何调用我们的函数?调用函数非常简单,我们首先要命名一个变量来接收我们的结果,然后再把结果输出来。
int result = Multiply(3, 2);
std::cout << result << std::endl;
Multiplication Log
让我们把它提升一个档次,假设我想做一堆乘法,我想将它们全部记录到控制台,如果我在没有函数的情况下做这样的事情,那么它看起来会很混乱.你可能认为我这样做是偶然的,但我实际上是故意这样做的,以证明这实际上一直在发生,人们复制和粘贴代码块,然后忘记更改一个小细节,在某些情况下,你实际上可能只是运行你的程序,甚至没有注意到它无法正常工作,直到它在某个地方崩溃了。
int result = Multiply(3, 2);
std::cout << result << std::endl;
int result1 = Multiply(5, 8);
std::cout << result1 << std::endl;
int result2 = Multiply(7, 4);
std::cout << result2 << std::endl;
std::cin.get();
}
我们还可以进一步优化它
MultiplyAndLog(int a, int b)
{
int result = Multiply(a, b);
std::cout << result << std::endl;
}
int main()
{
MultiplyAndLog(3, 2);
MultiplyAndLog(5, 8);
MultiplyAndLog(7, 4);
std::cin.get();
}
我可以摆脱所有这些代码,这就是我们最终得到一个干净且易于阅读的程序的原因。
函数要点
这是一个非常简单的例子,但我认为它有效地证明了函数真的很重要,你应该的目标是将你的代码分成许多许多函数,但是我想强调的一件事是不要过火,你不需要每一行都写一个函数,这对任何人都没有好处,他们很难维护你的代码,看起来凌乱和杂乱无章,它实际上会使你的程序变慢。
每次我们调用一个函数时,编译器都会生成一个调用指令,它的基本含义是,在正在运行的程序中,为了让我们调用一个函数,我们需要为函数创建整个堆栈帧意味着我们必须将参数之类的东西推送到堆栈上,我们还必须将称为返回地址的东西推送到堆栈上,然后我们所做的实际上是跳转到二进制文件的不同部分,以便开始执行函数中的指令以及我们推送的返回值,我们需要在调用函数之后回到原来的位置。
这被称为像在内存中跳转以执行函数指令一样,所有这些都需要时间,因此它会减慢我们的程序。现在我之前说星号的原因是,这一切都是假设编译器决定将我们的函数保留为实际函数并且不内联(inline)它我们将在未来深入讨论内联(inline)。
我之所以说这一切,你看你什么时候想创建,也不需要每一行都创建函数,需要一点经验才能意识到你需要一个函数做什么,但是如果你看到自己多次做一个常见的任务可以创建一个函数, 为此,函数的主要目的是防止代码重复,我们不想只是在任何地方复制和粘贴代码。
必须返回
除了Main函数之外,自定义的函数如果指定了返回类型,那一定就要有返回值。如果把Multiply 函数的返回值去掉,编译之后。编译器会报错,它会告诉我们必须要有返回值
main函数如果没有写返回值,它会自动返回0。这只是现代 C/C++ 版本的一个功能,可让您的代码更简洁一点。
还需要知道再debug模式下需要返回值。但是我们切换到release模式下,我们编译并不会报错。
这并不是说我们在这里做什么,说我们在这里所做的是正确的,因为如果我们确实捕获了该返回值,以便执行某些操作,我们将获得未定义的行为,就像编译器实际上不会对我们大喊大叫一样。但是,在启用某些调试编译标志的调试模式下,我们将收到一个错误,这只会帮助我们调试代码,因为任何时候您都应该编写一个函数,它会返回一些东西。
我们还通常将函数分解为声明和定义,声明通常是放在头文件里面, 我们在翻译单元或 Cpp 文件中编写定义.