【C++】命名空间&缺省参数&函数重载&引用&内联函数(二)

简介: 【C++】命名空间&缺省参数&函数重载&引用&内联函数

6-1.面试题:

  1. 为什么C语言支持函数重载,而C++支持函数重载?
  2. extern "C'的作用

6-1-1.为什么C语言不支持重载,C++支持?C++是如何支持的?---函数名修饰规则不同

备注:这里由于博主还没有干到LInux,就不能给大佬们演示linux下函数名修饰规则的具体内容了:

C 语言中:

3c6657a4884c442b983901bc66876a9e.pngC++中:

04d5666ff21148eca740b50c7cabdbd1.png

6-1-2.extern "C'的作用

6-1-2-1.什么是中间件程序?为什么会有extern "C"?

在写项目的时候,有的时候会用到中间件程序(配合可执行程序的一些组件):


通常我们就会把它编译成静态库或动态库(比如.dll).


如果这个中间件程序是用C++写的,但是整体的程序时用C语言写的,虽然在编译成二进制的指令的时候,C和C语言都没太大差异(因为此时已经经历了各自编译器的编译),但是由于C语言和C++的函数名修饰规则,整体程序在找中间件程序(组件)中的函数的时候就会表示找不到.这时extern "C"的作用就凸显出来了.


6-1-2-2.extern "C"的作用和为什么可以通过extern "C" 解决这个问题?


extern "C" 的作用:让C++用C的函数名规则去找函数地址.


基石:C++兼容C的语法,C++知道C语言的函数名规则,所以在有C和C++的函数名规则冲突的时候,在C++程序中使用extern "C" +函数声明  ,就可以解决这个问题.


6-1-2-3.extern "C"的使用场景举例:


下面以谷歌自己用C++写的tcmalloc代替mallc ,然后写成了一个中间件程序,后来一个C语言程序想用这个中间件程序代替mallc时他遇到的问题和解决办法:


baf3fe445fe24b42bd1ba43b39a29a3f.png


变式: 如果加完了extern "C",有同时有整体C++程序想使用这个被extern "C"修饰过了的中间件.这就可以将这个整体C++程序前加上extern "C".


7.引用

7-1.引用的基本使用(reference)

#include<iostream>
int main()
{
  int a = 10;
  int& b = a;//b是a的别名,b是a的引用
  printf("%d\n", b);
  b = 100;
  printf("%d\n", a);
}

fd4541528af14855ab9e202339ae12e9.png

注意:int& b=a;是取别名

而int* b=&a;是取地址

#include<iostream>
using namespace std;
void Swap(int* m, int* n)
{
  int temp = *m;
  *m = *n;
  *n = temp;
}
void Swap(int& m, int& n)
{
  int temp = m;
  m = n;
  n = temp;
}
int main()
{
  int a = 10;
  int b = 20;
  //传地址交换
  Swap(&a, &b);
  printf("a=%d\tb=%d\n", a, b);
  //传引用交换
  Swap(a, b);
  printf("a=%d\tb=%d\n", a, b);
  return 0;
}

6f1e25ac7fed4ca2ba6901d325cc3769.png


7-2.引用的特性 :

  1. 一个变量可以有多个别名
  2. 引用必须初始化(但是指针没有初始化的规定)
  3. 引用一旦引用了一个实体后,就不能再引用其他实体
int main()
{
  int a = 10;
  //一个变量可以有多个别名b,c,d
  int& b = a;
  int& c = a;
  int& d = a;
  //引用必须初始化
  int& e = a;
  //不是让e变成r的别名,而是把r赋值给e
  int r = 20;
  e = r;
  printf("%d\n", e);//20
  return 0;
}

7-3.常引用

int main()
{
    //const权限
    const int a = 10;//这里的a是可读不可写
  int& ra = a;//错,权限的放大不允许
  //错在把可读不可写的变量a给一个可读可写的引用
  const int& ra = a;//对
  int b = 10;//-可读可写
  const int& rb = b;//对,权限的缩小允许-可读不可写
  return 0;
}

只要是有类型差异在赋值转换时都会产生临时变量

转换:转换的是中间的临时变量,而不是c

  //隐式转换(权限的应用)
  int c = 10;
  double d = 1.1;
  d = c;//对,c语言隐式类型转换,但还是一样是有临时变量(double)类型
  //double& rc = c;//错,错因是因为是const double 类型的临时变量给了double类型的变量
  const double& rc = c;//对

8f3a5f2f75d34144baf15221c203f25c.png

备注:这里rc引用的不是C,因为类型差异(字节都不一样),rc引用的其实时中间的那个临时变量.

7-4.引用的场景

7-4-1.作参数

a4e59a225a4c4b049aef53d6e28f6bdb.png

7-4-2做返回值(传引用返回)

先看看之前我们学过的传值返回:

传值返回返回的是对象c的拷贝

0c0d0ec3c9564e6e8769081c92a3f8d2.png

这里说明了实际上是函数返回值是通过产生一个临时变量(const修饰)来临时保存,然后赋值给ret。

传引用返回:

传引用返回的是对象c的引用

8d9623811c764c82a33bc5fc4819657d.png

这里返回值是int&,也就是返回的是c的引用,也就是c的别名,然后c的别名又是ret的别名

函数栈帧问题:

int& Add(int a, int b)
{
  int c = a + b;
  return c;
}
int main()
{
  const int& ret = Add(1, 2);
  Add(5, 7);
  cout << ret << endl;//12
  return 0;
}


下面即是在main函数里没有用ret接收Add(5,7)的返回值,ret还是被改为了12,那是对因为ret是栈上已经销毁的变量c的引用 。


08846b80403842119757fd534e586ecd.png


但是如果是传值返回:调用了Add(5,7),还是3


1c3004a761694e5481308048d5013710.png


或者把c定义在static,静态常量区上:


655c192a3660453f80773ab29ce34532.png


越界不一定报错:


1.越界读基本不报错,因为编译器检查不出来


2.越界写,可能报错,而且是抽查,就像查酒驾,一般是查可能性大的地界查--抽查


传引用返回的优点:


因为传值返回传的是对象的拷贝,但是传引用返回是返回的是对象的别名,可以提高效率,这和传值调用和传址调用很像。


指针和引用的异同:

int main()
{
  int a = 10;
  //语法上,这里是给a起了一个别名,而是新定义了一个符号,并没有额外开空间
  int& ra = a;
  ra = 20;
  //语法上,这里是定义了内存是4个字节的变量存放a的地址
  int* pa = &a;
  *pa = 20;
  return 0;
}

实际从汇编实现的角度,引用的本质类似指针取地址的方式实现的(语法层和底层是隔离开的)---了解即可

指针和引用的不同点::

内存开辟角度(概念上)

初始化角度

实体对象更改角度

空指针角度

多级指针角度

引用更安全角度

8.内联函数

由C语言引入:

//C语言为了避免小函数开辟函数栈帧的开销--->提供了宏,预处理阶段展开
#define Add(x,y)  ((x)+(y))
int main()
{
  int x = 1, y = 2;
  int ret = Add(1, 2);
  printf("%d\n", ret);
  return 0;
}

C++推荐使用频繁的小函数,定义成inline函数,没有函数的开销,只是在调用的时候展开

内联函数:这里结合了宏没有函数开销的优点,同时又丢弃了宏复杂和不支持调试的缺点。

inline int Add(int a, int b)
{
  return a + b;
}
int main()
{
  int x = 1, y = 2;
  int ret = Add(1, 2);
  printf("%d\n", ret);
  return 0;
}

为什么不是将所有的函数定义成内联函数?(内联的缺陷)


1.因为内联函数的本质是通过通过牺牲展开函数,增加主函数代码量(指令变多,导致编译出来的程序变大,备注:指令变多不一定耗时长)来提高效率,而减少函数调用的开销,从而提高效率的。------>空间换时间所以适合将那些函数内部代码量比较少且频繁被调用的的函数定义成内联。当把大函数定义成内联时,编译器直接不搭理你的定义内联。备注:当调用1000次时,内联展开和调用函数的指令数是截然不同的。


2.内联不建议声明和定义分离,因为内联函数没有地址(直接展开了),会导致链接时找不到。


680a7d7af2f3402ab8f20b9382360ca8.png

目录
相关文章
|
3月前
|
程序员 C++
C++中的函数重载有什么作用
【10月更文挑战第19天】C++中的函数重载有什么作用
32 3
|
3月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
3月前
|
安全 程序员 编译器
【C++】如何巧妙运用C++命名空间:初学者必备指南
【C++】如何巧妙运用C++命名空间:初学者必备指南
|
3月前
|
编译器 程序员 C++
C++中的函数重载是什么
【10月更文挑战第19天】C++中的函数重载是什么
53 0
|
3月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
3月前
|
自然语言处理 编译器 Linux
【C++】巧用缺省参数与函数重载:提升编程效率的秘密武器
【C++】巧用缺省参数与函数重载:提升编程效率的秘密武器
|
11天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
51 18
|
11天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
37 13
|
11天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
37 5
|
11天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
27 5