06.函数

目录介绍
  • 6.1 概述和定义
    • 6.1.1 函数概念
    • 6.1.2 函数声明和定义
    • 6.1.3 函数分类
    • 6.1.4 Lambda函数
  • 6.2 函数参数
    • 6.2.1 值传递
    • 6.2.2 引用传递
    • 6.2.3 指针传递
  • 6.3 函数返回值
  • 6.4 函数重载
    • 6.4.1 函数重载案例
    • 6.4.2 重载注意事项
  • 6.5 内联函数
    • 6.5.1 内联函数是什么
    • 6.5.2 内联函数
    • 6.5.3 内联函数特点
    • 6.5.4 注意事项
    • 6.5.5 内联函数 vs 宏
    • 6.5.6 内联函数场景
  • 6.6 默认参数
    • 6.6.1 默认参数示例
    • 6.6.2 默认参数注意点

6.1 概述和定义

6.1.1 函数概念

函数 是一段可重用的代码块,用于执行特定的任务。函数可以提高代码的模块化、可读性和可维护性。C++ 函数包括函数声明、函数定义和函数调用。

一个函数通常包括以下部分:

  1. 返回类型:函数返回值的类型(如 intvoid 等)。
  2. 函数名:函数的名称,用于调用函数。
  3. 参数列表:函数接受的输入参数(可选)。
  4. 函数体:函数的具体实现代码。

6.1.2 函数声明和定义

语法

返回类型 函数名(参数列表) {
    // 函数体语句
    return 返回值; // 如果返回类型不是 void
}
  • 返回类型 :一个函数可以返回一个值。在函数定义中
  • 函数名:给函数起个名称
  • 参数列表:使用该函数时,传入的数据
  • 函数体语句:花括号内的代码,函数内需要执行的语句
  • return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据

示例

#include <iostream>
using namespace std;

// 函数声明
int add(int a, int b);

int main() {
    int result = add(3, 5); // 函数调用
    cout << "Result: " << result << endl; // 8
    return 0;
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

注意:函数的声明可以多次,但是函数的定义只能有一次

6.1.3 函数分类

常见的函数样式有4种

  1. 无参无返
  2. 有参无返
  3. 无参有返
  4. 有参有返

示例:

//1、 无参无返
void test01() {
    //void a = 10; //无类型不可以创建变量,原因无法分配内存
    cout << "this is test01" << endl;
    //test01(); 函数调用
}

//2、 有参无返
void test02(int a) {
    cout << "this is test02" << endl;
    cout << "a = " << a << endl;
}

//3、无参有返
int test03() {
    cout << "this is test03 " << endl;
    return 10;
}

//4、有参有返
int test04(int a, int b) {
    cout << "this is test04 " << endl;
    int sum = a + b;
    return sum;
}

int main() {
    test01();
    test02(10);
    int a3 = test03();
    int a4 = test04(1,3);
    return 0;
}

6.1.4 Lambda函数

C++11 引入了 Lambda 函数,用于定义匿名函数。

语法

[捕获列表](参数列表) -> 返回类型 {
    // 函数体
}

示例

#include <iostream>
using namespace std;

int main() {
    auto add = [](int a, int b) -> int {
        return a + b;
    };
    cout << "add(3, 5): " << add(3, 5) << endl; // 8
    return 0;
}

6.2 函数参数

函数可以接受零个或多个参数。参数可以是值传递、引用传递或指针传递。

6.2.1 值传递

传递参数的副本,函数内对参数的修改不会影响原始值。

  • 所谓值传递,就是函数调用时实参将数值传入给形参。
  • 值传递时,==如果形参发生,并不会影响实参==
void increment(int x) {
    x++;
    cout << "Inside function: " << x << endl; // 11
}

int main() {
    int a = 10;
    increment(a);
    cout << "Outside function: " << a << endl; // 10
    return 0;
}

在C++中,值传递(Pass by Value)是一种参数传递的方式,它指的是将实际参数的值复制给函数或方法的形式参数。在值传递中,函数或方法使用的是形式参数的副本,而不是直接操作实际参数本身。

  1. 形式参数是实际参数的副本:在函数或方法调用时,实际参数的值会被复制到对应的形式参数中。这意味着函数或方法内部对形式参数的修改不会影响到实际参数的值。
  2. 独立的内存空间:值传递会在内存中为形式参数分配独立的内存空间,这样函数或方法可以在其内部使用和修改这些副本,而不会影响到实际参数。
  3. 不会改变实际参数的值:由于值传递使用的是形式参数的副本,函数或方法对形式参数的修改不会影响到实际参数的值。
  4. 如果需要在函数或方法中修改实际参数的值,可以考虑使用引用传递或指针传递。

6.2.2 引用传递

传递参数的引用,函数内对参数的修改会影响原始值。

void increment(int &x) {
    x++;
    cout << "Inside function: " << x << endl; // 11
}

int main() {
    int a = 10;
    increment(a);
    cout << "Outside function: " << a << endl; // 11
    return 0;
}

6.2.3 指针传递

传递参数的地址,函数内通过指针修改原始值。

void increment(int *x) {
    (*x)++;
    cout << "Inside function: " << *x << endl; // 11
}

int main() {
    int a = 10;
    increment(&a);
    cout << "Outside function: " << a << endl; // 11
    return 0;
}

6.3 函数返回值

函数可以返回一个值,返回类型由函数声明指定。如果函数不需要返回值,可以使用 void示例

#include <iostream>
using namespace std;

int square(int x) {
    return x * x;
}

void printMessage() {
    cout << "This function has no return value." << endl;
}

int main() {
    int result = square(5);
    cout << "Square: " << result << endl; // 25
    printMessage();
    return 0;
}
6.7.2 函数占位参数

C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法: 返回值类型 函数名 (数据类型){}

在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术

示例:

//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int) {
    cout << "this is func" << endl;
}

int main() {
    func(10,10); //占位参数必须填补
    return 0;
}

6.4 函数重载

6.4.1 函数重载案例

函数重载(Function Overloading)是指在同一个作用域内,可以定义多个同名函数,但它们的参数列表不同。函数重载允许使用相同的函数名来实现不同的功能!

  1. 函数名相同:重载函数具有相同的函数名,但参数列表不同。
  2. 参数列表不同:参数列表可以通过参数的类型、个数或顺序的不同来区分。
  3. 返回类型不是重载的依据:函数重载不依赖于函数的返回类型,只依赖于参数列表。
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;
}

int main() {
    int result1 = add(3, 5); // 调用第一个add函数
    double result2 = add(2.5, 3.7); // 调用第二个add函数
    int result3 = add(1, 2, 3); // 调用第三个add函数
    return 0;
}

函数重载使得代码更加灵活,可以根据不同的需求使用相同的函数名来实现不同的功能。

6.4.2 重载注意事项

  • 引用作为重载条件
  • 函数重载碰到函数默认参数

示例:

//函数重载注意事项
//1、引用作为重载条件
void func(int &a) {
    cout << "func (int &a) 调用 " << a << endl;
}

void func(const int &a) {
    cout << "func (const int &a) 调用 " << a << endl;
}

//2、函数重载碰到函数默认参数
void func2(int a, int b = 10) {
    cout << "func2(int a, int b = 10) 调用" << a + b << endl;
}

void func2(int a) {
    cout << "func2(int a) 调用" << a << endl;
}

int main() {
    int a = 10;
    func(a); //调用无const
    func(10);//调用有const
    //func2(10); //碰到默认参数产生歧义,需要避免
    return 0;
}

6.5 内联函数

6.5.1 内联函数是什么

C++ 中的 内联函数(Inline Function) 是一种优化技术,用于减少函数调用的开销。

通过将函数体直接插入到调用处,内联函数可以避免函数调用的额外开销(如栈帧的创建和销毁),从而提高程序的执行效率。

6.5.2 内联函数定义

在函数声明或定义前加上 inline 关键字:编译器会尝试将函数调用替换为函数体,以减少函数调用的开销。

inline 返回类型 函数名(参数列表) {
    函数体
}

示例 1:基本用法

#include <iostream>
using namespace std;

// 定义内联函数
inline int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 5); // 调用内联函数
    cout << "Result: " << result << endl;
    return 0;
}

输出

Result: 8

说明:编译器会将 add(3, 5) 替换为 3 + 5,从而避免函数调用。

示例 2:类中的内联函数

在类中定义的成员函数默认是内联的。

#include <iostream>
using namespace std;

class Math {
public:
    // 内联成员函数
    inline int multiply(int a, int b) {
        return a * b;
    }
};

int main() {
    Math math;
    int result = math.multiply(4, 5); // 调用内联函数
    cout << "Result: " << result << endl;
    return 0;
}

输出

Result: 20

6.5.3 内联函数特点

  1. 减少函数调用开销:内联函数将函数体直接插入到调用处,避免了函数调用的额外开销。
  2. 适用于小型函数:内联函数通常用于代码量较小的函数,因为过大的函数会导致代码膨胀。
  3. 由编译器决定inline 关键字只是对编译器的建议,编译器可以选择忽略内联请求。

6.5.4 注意事项

  1. 代码膨胀:内联函数会将函数体插入到每个调用处,如果函数体较大,会导致代码膨胀,反而降低性能。
  2. 递归函数不能内联:递归函数无法完全展开,因此不能使用内联。
  3. 虚函数不能内联:虚函数的调用需要在运行时确定,因此不能内联。
  4. 编译器决定inline 关键字只是建议,编译器可能会忽略内联请求。

6.5.5 内联函数 vs 宏

内联函数是 C++ 对 C 语言宏(#define)的改进,具有以下优势:

  1. 类型安全: 内联函数是类型安全的,而宏没有类型检查。
  2. 调试方便: 内联函数可以调试,而宏在预处理阶段被替换,无法调试。
  3. 作用域规则: 内联函数遵循作用域规则,而宏是全局的。

6.5.6 内联函数场景

  1. 小型函数: 函数体较小且频繁调用时,使用内联函数可以提高性能。
  2. 性能关键代码: 在性能关键路径上,使用内联函数可以减少函数调用开销。
  3. 替代宏: 在需要类型安全和调试支持的场景中,使用内联函数替代宏。

6.6 默认参数

6.6.1 默认参数示例

函数默认参数是一种允许在函数声明中为某些参数指定默认值的机制。如果调用函数时没有为这些参数提供实参,那么函数会自动使用默认值。

语法: 返回值类型 函数名 (参数= 默认值){}

示例:

int func(int a , int b =10 , int c= 10) {
    return a + b + c;
}

//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
    return a + b;
}

int main() {
    cout << "ret = " << func(20, 20) << endl;
    cout << "ret = " << func(100) << endl;
    cout << "ret = " << func2(10,20) << endl;
    cout << "ret = " << func2(10) << endl;
    return 0;
}

调用函数时的行为

  1. 如果调用函数时没有提供某个参数的值,函数会使用默认值。
  2. 如果提供了参数值,则会覆盖默认值。

6.6.2 默认参数注意点

从右向左设置默认参数,默认参数必须从右到左依次设置,不能跳过。例如:

void example(int a, int b = 10, int c = 20); // 合法
void example(int a = 10, int b, int c = 20); // 不合法

与函数重载的冲突,默认参数可能与函数重载产生冲突,导致编译器无法确定调用哪个函数。例如:

void example(int a);
void example(int a, int b = 10);
example(5); // 编译器无法确定调用哪个函数
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]