目录
一、函数的基本概念
1.1、构成
1.2、声明和定义
1.3、函数的调用
二、参数
2.1、形参和实参
2.2、参数的传递
传值
传引用
传指针
三、C++函数的一些新特性
3.1、Lambda表达式
3.2、右值引用
3.3、默认参数
3.4、变长参数模板
3.5、constexpr函数
3.6、noexcept函数
四、C++内联函数
五、函数的重载
C++函数是一段可重用的代码,用于执行特定的任务。它可以接受输入参数、执行一些操作,最后返回一个值。函数可以通过模块化编程来简化代码,提高代码的可读性和可维护性。函数也被称作方法。
函数分为库函数(也称标准函数库或系统函数库)和自定义函数两种。
库函数是指在C++标准库或其他系统库中提供的函数,通常包含在头文件中。C++标准库中包含了很多常用的函数和数据类型,例如字符串处理、输入输出、数学计算、容器和算法等等。开发者可以直接使用这些库函数,而无需重新编写代码,从而提高开发效率和代码可读性。
自定义函数是由开发者自行编写的函数,用于实现特定的功能。自定义函数可以在程序中多次调用,避免了代码的重复编写。自定义函数的定义可以在main函数之前或之后,甚至在其他文件中,只要在使用函数之前有函数声明即可。
本文主要解释自定义函数。
函数的构成:
返回类型 函数名(参数列表) {函数体return 返回值;}
其中:
函数可以先声明再定义,或者直接定义。函数的声明和定义可以分别在不同的源文件中实现,这种方式可以方便地实现模块化编程和代码复用。
函数声明:函数声明通常包括函数名、参数列表和返回类型,它告诉编译器这个函数的存在和类型,以便编译器在调用该函数时能够正确地生成调用代码。函数声明可以出现在头文件中,也可以在函数定义之前进行。
函数定义:函数定义包括函数名、参数列表、返回类型和函数体,它实现了函数的具体功能。函数定义必须出现在程序的某个位置,通常放在源文件的末尾。
以下是一个使用函数声明和定义的例子:
// 声明函数int addFun(int x, int y);// 定义函数int addFun(int x, int y) {return x + y;}
C++函数通过函数名和参数列表来调用。调用函数时,需要将实际参数传递给函数。实际参数是调用函数时提供的参数。
以下是调用函数的语法:
函数名(实际参数列表);
例如,要调用上面定义的 addFun 函数,可以这样写:
int main() {int a = 10, b = 20;int c = addFun(a, b);return 0;}
C++函数的参数分为形式参数和实际参数。形式参数是定义在函数声明或定义中的参数,也称为形参。实际参数是在调用函数时传递给函数的值或变量,也称为实参。
需要注意的是,函数的实参可以是常量、变量、表达式、指针、引用等类型,而函数的形参通常是变量或引用类型。在函数调用时,实参会被传递给形参,如果实参和形参的类型不匹配,编译器会尝试进行类型转换。如果无法进行类型转换,编译器将会报错。
此外,C++还支持默认参数,它允许在函数定义时为某些参数设置默认值,这样在函数调用时可以省略这些参数。例如:
int addFun(int a, int b = 0) {return a + b;}int main() {int x = 10, y = 20;int result1 = addFun(x); // b参数使用默认值0int result2 = addFun(x, y); // b参数使用传递的实参yreturn 0;}
C++支持三种参数传递方式:传值、传引用或传指针。
传值是将实际参数的值复制到形式参数的过程。在函数中修改形式参数的值不会影响实际参数的值。这种方式适用于参数较少、参数类型简单的情况,也是使用最广泛的情况。
以下是传值调用的示例:
void addplus(int a) {a++; // 修改形式参数的值}int main() {int x = 10;addplus(x); // 传递 x 的值cout << x << endl; // 输出 10,x 的值没有改变return 0;}
传引用是指将实参的地址传递给形参,函数内部可以直接操作实参的值。传引用方式可以避免复制大量的数据,提高程序的效率。传引用方式使用引用符号 & 定义形参。
以下是传引用的示例:
void addplus(int& a) {a++; // 通过引用修改实际参数的值}int main() {int x = 10;addplus(x); // 传递 x 的别名cout << x << endl; // 输出 11,x 的值已经被修改return 0;}
传指针是指将实参的地址传递给形参,函数内部可以通过指针来操作实参的值。传指针方式可以与传引用方式相比,更加灵活,但需要注意指针为空的情况。传指针方式使用指针符号 * 定义形参。
以下是传指针的示例:
void addplus(int* a) {(*a)++; // 通过指针修改实际参数的值
}int main() {int x = 10;addplus(&x); // 传递 x 的地址cout << x << endl; // 输出 11,x 的值已经被修改return 0;
}
注意,传引用通常比传指针更易于使用和更安全,因为它可以防止空指针异常。
Lambda表达式是一种匿名函数,可以在需要时快速创建一个函数对象。Lambda表达式可以访问它所在函数的变量,这使得它们非常灵活和有用。
以下是一个使用Lambda表达式的例子:
#include
#include
#include int main() {std::vector nums = {1, 2, 3, 4, 5};std::for_each(nums.begin(), nums.end(), [](int num) {std::cout << num << " ";});return 0;
}
在这个例子中,Lambda表达式被传递给了std::for_each算法,用于输出向量中的所有元素。Lambda表达式的参数类型和返回类型都可以被自动推断出来。
右值引用是一种新的引用类型,可以将右值转换为左值,并提高代码的性能和效率。
以下是一个使用右值引用的例子:
#include
#include std::string&& foo(std::string& str) {return std::move(str);
}int main() {std::string str = "hello, world!";std::string&& rstr = foo(str);std::cout << rstr << std::endl;return 0;
}
在这个例子中,foo函数返回一个右值引用,将传递给它的字符串转换为右值。在main函数中,我们使用一个右值引用变量来接收foo函数的返回值,并输出它。
默认参数允许在函数定义中为某些参数设置默认值,在函数调用时可以省略这些参数。
以下是一个使用默认参数的例子:
#include
#include void foo(std::string str = "hello, world!") {std::cout << str << std::endl;
}int main() {foo(); // 输出:hello, world!foo("hi, there!"); // 输出:hi, there!return 0;
}
在这个例子中,foo函数的参数str被设置了默认值"hello, world!"。当我们在main函数中调用foo函数时,如果不传递任何参数,则使用默认值;否则,使用传递的参数值。
可变参数模板允许在函数中使用任意数量的参数,这对于处理不定数量的参数非常有用。
以下是一个使用可变参数模板的例子:
#include
#include template
void print(T arg) {std::cout << arg << std::endl;
}template
void print(T arg, Args... args) {std::cout << arg << " ";print(args...);
}int main() {print("hello", "world", "!");print(1, 2, 3, 4, 5);return 0;
}
constexpr函数是一种特殊的函数,可以在编译时求值,这可以提高程序的效率和性能。
以下是一个使用constexpr函数的例子:
#include constexpr int factorial(int n) {return n <= 1 ? 1 : n * factorial(n - 1);
}int main() {constexpr int n = 5;constexpr int result = factorial(n);std::cout << result << std::endl;return 0;
}
noexcept函数是一种指定函数不抛出异常的方法,这可以帮助编译器优化代码并提高程序的性能。
以下是一个使用noexcept函数的例子:
#include
#include void foo(std::vector& v) noexcept {v.push_back(42);
}int main() {std::vector v = {1, 2, 3};foo(v);for (auto i : v) {std::cout << i << " ";}return 0;
}
C++中,内联函数是一种特殊的函数,编译器会尝试将其在调用点直接展开,以避免函数调用的开销。
内联函数通常是通过在函数定义前加上inline关键字来定义的。在调用内联函数时,编译器会直接将函数体插入到调用点处,而不会生成函数调用的代码。这种展开的方式可以减少函数调用的开销,从而提高程序的性能。
需要注意的是,内联函数适用于函数体比较简单的函数。对于函数体比较复杂的函数,内联展开可能会导致代码膨胀,反而降低程序的性能。此外,编译器并不一定会将函数展开为内联函数,具体是否展开取决于编译器的实现。
下面是一个内联函数的例子:
inline int add(int a, int b) {return a + b;}
需要注意的是,内联函数仅仅是一种建议,编译器并不一定会将其展开为内联函数。
此外,对于在类定义中定义的成员函数,默认情况下也是内联函数,类定义中定义的成员函数会隐式地添加inline关键字。因此,对于类定义中定义的成员函数,不需要显式地添inline关键字。
C++中,函数重载(Function Overloading)指的是在同一个作用域内定义的具有相同名称但参数类型、个数或顺序不同的多个函数。在调用函数时,编译器会根据实参的类型、个数和顺序,自动选择合适的函数进行调用。
函数重载的优点在于可以方便地为函数提供不同的参数选项,同时能够避免函数名称冲突的问题。
下面是一个函数重载的例子:
int add(int a, int b) {return a + b;}double add(double a, double b) {return a + b;}int add(int a, int b, int c) {return a + b + c;}
需要注意的是,函数重载的区分是基于参数类型、个数和顺序的,而不是基于返回值类型的。因此,在定义函数时,不能只依据返回值类型的不同来进行函数重载。