C++基础知识【4】函数及参数

目录

一、函数的基本概念

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函数之前或之后,甚至在其他文件中,只要在使用函数之前有函数声明即可。

        本文主要解释自定义函数。

1.1、构成

        函数的构成:

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

    函数体

    return 返回值;

}

其中:

  • 函数名:函数名是函数的标识符,用于标识函数并区分其他函数。在C++中,函数名由字母、数字和下划线组成,不能以数字开头。
  • 参数列表:参数列表是一组用逗号分隔的变量,函数在执行时需要这些变量的值。C++中的函数可以有零个或多个参数,参数可以有默认值,也可以是引用类型。
  • 返回类型:返回类型指定函数返回的值的类型。C++中的函数可以返回任何类型的值,包括基本数据类型、结构体、类、指针等。
  • 函数体:函数体是包含一组语句的代码块,它定义了函数要执行的操作。

1.2、声明和定义

        函数可以先声明再定义,或者直接定义。函数的声明和定义可以分别在不同的源文件中实现,这种方式可以方便地实现模块化编程和代码复用。

        函数声明:函数声明通常包括函数名、参数列表和返回类型,它告诉编译器这个函数的存在和类型,以便编译器在调用该函数时能够正确地生成调用代码。函数声明可以出现在头文件中,也可以在函数定义之前进行。

        函数定义:函数定义包括函数名、参数列表、返回类型和函数体,它实现了函数的具体功能。函数定义必须出现在程序的某个位置,通常放在源文件的末尾。

以下是一个使用函数声明和定义的例子:

// 声明函数

int addFun(int x, int y);

// 定义函数

int addFun(int x, int y) {

    return x + y;

}

1.3、函数的调用

        C++函数通过函数名和参数列表来调用。调用函数时,需要将实际参数传递给函数。实际参数是调用函数时提供的参数。

以下是调用函数的语法:

函数名(实际参数列表);

例如,要调用上面定义的 addFun 函数,可以这样写:

int main() {

    int a = 10, b = 20;

    int c = addFun(a, b);

    return 0;

}

二、参数

2.1、形参和实参

        C++函数的参数分为形式参数和实际参数。形式参数是定义在函数声明或定义中的参数,也称为形参。实际参数是在调用函数时传递给函数的值或变量,也称为实参。

        需要注意的是,函数的实参可以是常量、变量、表达式、指针、引用等类型,而函数的形参通常是变量或引用类型。在函数调用时,实参会被传递给形参,如果实参和形参的类型不匹配,编译器会尝试进行类型转换。如果无法进行类型转换,编译器将会报错。

        此外,C++还支持默认参数,它允许在函数定义时为某些参数设置默认值,这样在函数调用时可以省略这些参数。例如:

int addFun(int a, int b = 0) {

    return a + b;

}

int main() {

    int x = 10, y = 20;

    int result1 = addFun(x); // b参数使用默认值0

    int result2 = addFun(x, y); // b参数使用传递的实参y

    return 0;

}

2.2、参数的传递

        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;
}

注意,传引用通常比传指针更易于使用和更安全,因为它可以防止空指针异常。

三、C++函数的一些新特性

3.1、Lambda表达式

        Lambda表达式是一种匿名函数,可以在需要时快速创建一个函数对象。Lambda表达式可以访问它所在函数的变量,这使得它们非常灵活和有用。

以下是一个使用Lambda表达式的例子:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> 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表达式的参数类型和返回类型都可以被自动推断出来。

3.2、右值引用

        右值引用是一种新的引用类型,可以将右值转换为左值,并提高代码的性能和效率。

以下是一个使用右值引用的例子:

#include <iostream>
#include <string>

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函数的返回值,并输出它。

3.3、默认参数

        默认参数允许在函数定义中为某些参数设置默认值,在函数调用时可以省略这些参数。

以下是一个使用默认参数的例子:

#include <iostream>
#include <string>

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函数时,如果不传递任何参数,则使用默认值;否则,使用传递的参数值。

3.4、变长参数模板

        可变参数模板允许在函数中使用任意数量的参数,这对于处理不定数量的参数非常有用。

以下是一个使用可变参数模板的例子:

#include <iostream>
#include <string>

template <typename T>
void print(T arg) {
    std::cout << arg << std::endl;
}

template <typename T, typename... Args>
void print(T arg, Args... args) {
    std::cout << arg << " ";
    print(args...);
}

int main() {
    print("hello", "world", "!");
    print(1, 2, 3, 4, 5);
    return 0;
}

3.5、constexpr函数

        constexpr函数是一种特殊的函数,可以在编译时求值,这可以提高程序的效率和性能。

以下是一个使用constexpr函数的例子:

#include <iostream>

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;
}

3.6、noexcept函数

        noexcept函数是一种指定函数不抛出异常的方法,这可以帮助编译器优化代码并提高程序的性能。

以下是一个使用noexcept函数的例子:

#include <iostream>
#include <vector>

void foo(std::vector<int>& v) noexcept {
    v.push_back(42);
}

int main() {
    std::vector<int> v = {1, 2, 3};
    foo(v);

    for (auto i : v) {
        std::cout << i << " ";
    }

    return 0;
}

四、C++内联函数

        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;

}

        需要注意的是,函数重载的区分是基于参数类型、个数和顺序的,而不是基于返回值类型的。因此,在定义函数时,不能只依据返回值类型的不同来进行函数重载。

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从此不归路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值