五、函数

一、内联函数

1.1 内联函数的引出

1.1.1 宏函数的缺陷

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

#define MyAdd(x, y) x+y
#define MyCompare(a,b) (a)<(b)?(a):(b)

void test01()
{
    int ret = MyAdd(10, 20);
    cout << "ret = " << ret << endl;  // ret = 30
    ret = MyAdd(10, 20) * 20;  // 预期结果是 600
    // 实际上结果是 410 = 10 + 20 * 20 = 410, 所以宏定义的时候应该加上括号 #define MyAdd(x, y) (x+y)
    cout << "ret = " << ret << endl;  // ret = 410
}

void test02()
{
    int a = 10;
    int b = 20;
    int ret = MyCompare(a, b);
    cout << "ret = " << ret << endl;  // ret = 10
    ret = MyCompare(++a, b);   // 预期结果是 11
    // 实际上结果是 12  (++a)<(b)?(++a):(b)
    // 刚开始 ++a之后, a=11, 返回的时候又一次++a, a=12
    cout << "ret = " << ret << endl;  // ret = 12
}

int main()
{
    test01();
    test02();
    return EXIT_SUCCESS;
}

 

1.1.2 宏的缺陷弥补(转换为函数)

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void mycompare(int a, int b)
{
    int ret = a < b ? a : b;
    cout << "ret = " << ret << endl;
}

void test03()
{
    int a = 10;
    int b = 20;
    mycompare(++a, b);  // ret = 11
}

int main()
{
    test03();
    return 0;
}

1.1.3 内联函数的基本概念

        在 C++ 中,预定义宏的概念是用内联函数来实现的。 而内联函本身也是一个真正个函数。内联函数有普通函数的所有行为。唯一不同的是在于内联函数会在适当的地方预定义宏一样的展开,所以不需要函数调用的开销,因此不应该使用宏,而应该使用内联函数。

// 在普通函数(非成员函数)前面加上 inline关键字就成为了内联函数
// inline void fun(int a) // 这样的写法没有任何效果,只是声明函数
inline void fun(int a)
{
    return a++;  // 内联函数必须要和函数体结合在一起才会起作用
}
/*
     注意:编译器将会检查函数参数列表使用是否正确, 并返回值(进行必要的转换)。之这些事是预处理器无法完成的。
     内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省却了函数调用时候的压栈,跳转,返回的开销,我们可以理解为内联函数是以空间换时间。
*/// 注意: 内联函数的声明
inline void fun();
void fun() { };  // 如果函数实现的时候,没有加上 inline, 那么这个函数依然不算内联函数

1.2 类中的内联函数

为了定义内联函数,一般会在函数名称前面放一个inline。但是类内部定义的内联函数并不是必须要加inline的,任何在类内部定义的函数都会默认加上inline自动成为内联函数。

1.3 内联函数和编译器

C++ 内联函数也会有一些限制,以下情况比那一起可能不会将函数进行内联编译
    1. 不能存在任何形式的循环语句
    2. 不能存在过多条件判断语句
    3. 函数体不能过于庞大
    4. 不能对函数进行取值操作


内联只是编译器一个建议,编译器不一定会接受这种建议。如果没有将函数声明位内联函数,那么编译器也可能将函数作为内联函数,一个好的编译器会自动内联小的,简单的函数、

二、函数的默认参数

    C++在生命函数原型的时候可以为一盒或者多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值替代。 C语言中是没有默认参数

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

// 函数的默认参数, 在参数后面 = ... 
// 函数参数的注意事项:如果有一个位置有了默认参数,那么从这个位置开始,后面的都必须要有默认参数
// 传入参数,如果有参数,就用传入的参数,没有参数就用默认值
void fun01(int a = 10, int b = 10)
{
    cout << "a + b = " << a + b << endl;
}

void fun02(int a, int b = 10, int c = 20)
{
    cout << "a + b + c = " << a + b + c<< endl;
}

void test01()
{
    fun01();  //  a + b = 20
    fun02(1); // a + b + c = 31
    fun02(1, 2); // a + b + c = 23

}

int main()
{
    test01();
    return EXIT_SUCCESS;
}
// 如果函数声明里面有了默认参数, 函数实现时候必须没有默认参数。 也就是函数声明和实现里只能有一个默认参数,不要同时出现默认参数。
// 如:
void Func(int a = 10; int b = 20);  //  声明

void Func(int a = 10; int b = 20)   // 实现的时候编译就会报错
{
    
}

三、函数的占位参数

// C语言中没有占位参数

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

// int 后面没有形参就是展位参数。函数调用的时候必须要提供这个参数,但是用不到
// 占位参数没有什么大用途, 只要重载 ++ 符号才有点用
// 占位参数可以有默认值, 如 int = 1
void func(int a, int)
{
    ;
}
int main()
{
    func(10, 1);
    return EXIT_SUCCESS;
}

四、函数重载

4.1 函数重载基本语法

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

// 函数重载
// C++中函数名称可以重复
// 必须在同一个作用域下, 函数名称相同
// 函数的参数个数不同,或者类型不同,或者顺序不同
// 函数返回值不同不能作为函数重载的条件

void fun()
{
    cout << "无参数的func" << endl;
}

void fun(int a)
{
    cout << "有参数的func(int a)" << endl;
}

void fun(double a)
{
    cout << "有参数的func(double a)" << endl;
}

void fun(double a, int b)
{
    cout << "有参数的func(double a, int b)" << endl;
}

void fun(int a, double b)
{
    cout << "有参数的func(int a, double b)" << endl;
}

void test01()
{
    fun();
    fun(1);
    fun(1.1);
    fun(1.1, 3);
    fun(3, 1.1);
}

int main()
{
    test01();
    return EXIT_SUCCESS;
}

4.2 函数重载与默认参数

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

// 函数重载碰到了默认参数的时候,要注意避免二义性的问题
void func02(int a, int b = 10)
{

}

void func02(int a)
{

}

void test()
{
    // func02(10); // 报错,不知道调用哪一个
}
int main()
{
    test();
    return EXIT_SUCCESS;
}

4.3 引用的函数重载

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void func03(int &a)
{
    cout << "int &a" << endl;
}

void func03(const int &a)  // const 也是可以作为重载的条件
{
    cout << "const int &a" << endl;
}

void test03()
{
    int a = 10;
    func03(a); //  int &a
    func03(10); // const int &a
}


int main()
{
    test03();
    return EXIT_SUCCESS;
}

4.4 函数重载的实现原理

    编译器为了实现函数重载,也是默认为我们做了一些幕后工作,编译器用不同的残花类型来修饰不同的函数名, 比如 void fun(); 编译器可能会将函数名修饰成 _func, 当编译器碰到 void func(int a), 编译器可能将函数名修饰为 _func_int, 当编译器碰到 void func(int x, char y), 编译器可能会将函数名修饰为 _func_int_char 。我们使用 "可能",是因为编译器如何修饰重载的函数名并没有一个统一的标准,所以不同的编译器可能会产生不同的内部名称。

// 在linux中
void func() {};
void func(int x) {};
void func(int x, char y) {};

// 在linux中编译过后的函数名为
_Z4funcv   // v代表void
_Z4funci   // i代表int
_Z4funcic  // i代表第一个参数int,c代表第二个参数char

 

五、extern "C" 浅析

5.1 extern "C" 使用

// test.h
#pragma once
#include<stdio.h>
void show();


// test.c
#include "test.h"

void show()
{
    printf("Hello World\n");
}
// main.c
#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
//#include "test.h"
using namespace std;

// C++中想调用C语言方法

extern "C"  void show();// 代表show方法按照 C语言来处理。使用了 外部的 show,但是 #include "test.h" 又包含了 show',所以 #include "test.h" 就可以不需要了

// 如果 需要的C的函数太多了,如果每一个函数都这样 extern "C" 这样就太复杂了, 所以我们都是在 .h 中直接修改,具体修改方式见 5.2 节
int main()
{
    show();  // 直接调用show()的时候,报错, 无法解析的外部符号 
    // 在 C++中,函数是可以发生重载的,编译器会把这个函数名偷偷改变, 如可能改称为 _showv了,所以调用show的时候报错
    return EXIT_SUCCESS;
}

 

5.2 extern "C" 的完善

如果 需要的C的函数太多了,如果每一个函数都这样 extern "C" 这样就太复杂了, 修改如下

// test.h
#pragma once


#ifdef __cplusplus // 两个 _
extern "C" {
#endif  // !__cplusplus

#include<stdio.h>
    void show();
    
    // 其他的C语言函数

#ifdef __cplusplus // 两个 _
}
#endif  // !__cplusplus
// main.c 修改为

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include "test.h"  // 此时需要这个了
using namespace std;

int main()
{
    show();  // 直接调用show()的时候,报错, 无法解析的外部符号 
    // 在 C++中,函数是可以发生重载的,编译器会把这个函数名偷偷改变, 如可能改称为 _showv了,所以调用show的时候报错
    return EXIT_SUCCESS;
}

 

posted on 2022-01-18 20:33  软饭攻城狮  阅读(51)  评论(0编辑  收藏  举报

导航