C++ 笔记(13)— 函数(函数声明、函数定义、函数调用[传值、指针、引用]、函数参数默认值、函数重载)

每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。

1. 函数声明

函数声明告诉编译器函数的名称、返回类型和参数。函数声明包括以下几个部分:

return_type function_name( parameter list );

函数声明示例:

int max(int num1, int num2);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:

int max(int, int);

函数可接受用逗号分隔的多个参数,但只能有一种返回类型。编写不需要返回任何值的函数时,可将其返回类型指定为 void

当在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,应该在调用函数的文件顶部声明函数。

2. 函数定义

函数定义提供了函数的实际主体。 C++ 中的函数定义的一般形式如下:

return_type function_name( parameter list )
{
   body of the function
}

C++ 中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:

  • 返回类型:一个函数可以返回一个值。 return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下, return_type 是关键字 void
  • 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • 函数主体:函数主体包含一组定义函数执行任务的语句。

3. 函数调用

当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。例如:

#include <iostream>
using namespace std;
 
// 函数声明
int max(int num1, int num2);
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
   int ret;
 
   // 调用函数来获取最大值
   ret = max(a, b);
 
   cout << "Max value is : " << ret << endl;
 
   return 0;
}
 
// 函数返回两个数中较大的那个数
int max(int num1, int num2) 
{
   return (num1 > num2)? num1:num2; 
}

3.1 传值

传值调用是把参数的实际值复制给函数的形式参数。默认情况下, C++ 使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。

一个函数 f() 的参数实际上是 f() 中的局部变量, 每次 f() 被调用时都会初始化 。

// 传值方式 (将待传值拷贝一份, 交给函数 )
// 传值方式非常直接 , 其代价就是复制值的开销
int f(int x)
{
    x = x + 1;
    return x;
}

int main ()
{
    int a = 0;
    std::cout << f(a) << std::endl; // 1
    std::cout << a << std::endl; // 0  f() 不 改 变 a

    int b = 10;
    std::cout << f(b) << std::endl; // 11 
    std::cout << b << std::endl; // 10 f() 不 改 变 b
}

3.2 传常量引用

传常量引用,不会有复制值开销,其中符号 & 表示 “ 引用 ”,而此处的 const 用来阻止在函数 f 中修改传入的参数。如果修改传入的参数则会报错 assignment of read-only reference ‘x’

int f(const int& x)
{
    std::cout << "in f function x is " << x << std::endl;
    // x = x + 1;   error: assignment of read-only reference ‘x’
    return x;
}

int main ()
{
    int a = 0;
    std::cout << f(a) << std::endl; // 0  

    int b = 10;
    std::cout << f(b) << std::endl; // 10 
}

3.3 传引用

如果我们确实希望函数修改其参数,那么传引用方式把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

// 传引用
int f(int& x)
{ 
    x = x + 1; 
    std::cout << "in f function x is " << x << std::endl;
    return x;
}

int main ()
{
    int a = 0;
    std::cout << f(a) << std::endl; // 1  
    std::cout << a << std::endl; // 1 


    int b = 10;
    std::cout << f(b) << std::endl; // 11 
    std::cout << b << std::endl; // 11 
}

这里 , 我们希望 f 函数修改参数 x ,因此我们没有使用传值参数 ( 拷贝参数值 ) , 也没有使用传常量引用参数 ( 不允许修改参数 ) ,只是将实际参数的 “ 简单引用 ” 传递给形参 。

示例 2

int main ()
{
    int i = 7;
    int& r = i; // r 是 i 的引用,也就是说 , 任何对 r 的使用实际上使用的是 i
    r = 9;
    std::cout << i << std::endl; // 9 
    i = 10;
    std::cout << r << std::endl; // 10 
}

3.4 指针调用

该方法把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

#include <iostream>

void swap(int *x, int *y)
{
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

int main()
{
    int a = 1;
    int b = 2;
    std::cout << "交换前 a is " << a << " b is " << b << std::endl;
    /* 调用函数来交换值
    * &a 表示指向 a 的指针,即变量 a 的地址 
    * &b 表示指向 b 的指针,即变量 b 的地址 
    */
    swap(&a, &b);
    std::cout << "交换后 a is " << a << " b is " << b << std::endl;

    return 0;
}


输出结果:

交换前 a is 1 b is 2
交换后 a is 2 b is 1

3.5 传值、传常量引用、传引用、传指针使用原则

  • 使用传值方式传递非常小的对象;
  • 使用传常量引用方式传递你不需修改的大对象;
  • 让函数返回一个值,而不是修改通过引用参数传递来的对象;
  • 只有迫不得已时才使用传引用方式;

第三条规则表达的是, 当你想用函数改变一个变量的值时, 实际上你还有另一种选择 。
考虑如下代码 :

    int incr1(int a) 
    { 
        return a + 1;   // 返回新的值作为结果
    }

    void incr2(int& a) 
    { 
        ++a; //通过传引用修改对象
    } 
    
    
    int x = 7;
    x = incrl(x);  // 意义非常明显
    incr2(x); //相当晦涩难懂

那么我们为什么还需要非常量引用传递方式呢 ? 因为有时候这种参数传递方式是必要的 。

  • 用于操作容器 ( 比如 vector ) 和其他大的对象 。
  • 用于改变多个对象的函数(函数只能有一个返回值) 。

4. 函数参数默认值

定义函数时可以为参数列表中后边的每一个参数指定默认值。调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值。请看下面的实例:

#include <iostream>
using namespace std;

int mySum(int, int y);

int mySum(int x, int y= 100)
{
    return x + y;
}

int main() 
{
    int a = 10;
    int b = 20;
    cout << "not default value result: "<<mySum(a, b) << endl;
    cout << "use default value result: "<<mySum(a) << endl;

    return 0;
}

参考:
https://blog.csdn.net/kanglongyao/article/details/47184593?utm_source=blogxgwz6

5. 函数重载

名称和返回类型相同,但参数不同的函数被称为重载函数。在应用程序中,如果需要使用不同的参数调用具有特定名称和返回类型的函数,重载函数将很有用。

假设您需要编写一个计算圆和圆柱的面积的应用程序。计算圆面积的函数需要一个参数—半径,而计算圆柱面积的函数除了需要圆柱的半径外,还需要圆柱的高度。这两个函数需要返回的数据类型相同,都是面积。C++ 让您能够定义两个重载的函数,它们都叫 Area,都返回double,但一个接受半径作为参数,另一个接受半径和高度作为参数。

#include <iostream>
using namespace std;

const double Pi = 3.14159265;
double Area(double radius); // for circle
double Area(double radius, double height); // for cylinder

// for circle
double Area(double radius)
{
    return Pi * radius * radius;
}

// overloaded for cylinder
double Area(double radius, double height)
{
// reuse the area of circle
    return 2 * Area (radius) + 2 * Pi * radius * height;
}

int main()
{
    cout << "圆面积是 " << Area(3) << endl;
    cout << "圆柱面积是 " << Area(3,5) << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wohu007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值