函数重载、引用再探、内联函数

文章目录

  • 函数重载
  • 为什么C++支持重载,C语言不支持呢?
  • extern “C”
  • 引用再探
    • 引用的特性
    • 引用的使用场景
    • 引用和指针
    • 引用和指针的不同点:
  • 内联函数
    • 什么是内联函数?
    • 内联函数的特性
    • 内联函数的好处
    • 类的内联成员函数的声明
    • 内联函数的使用
  • constexpr函数
    • 概念
    • 特征
    • 内联函数和constexpr函数放在头文件内


函数重载

在同一个作用域下,对于相同的函数名,函数的参数类型不同,参数顺序不同,参数的个数不同, 都可以形成函数的重载参数名不同,返回值不同不形成重载)

函数的重载主要用于处理功能相同,形参类型不同的数据。

void test(int i, int j)
{cout << "test" << endl;
}void test(double i, int j) // 类型不同
{cout << "test" << endl;
}void test(int j, double i) // 顺序不同
{cout << "test" << endl;
}void test(double i, int j, int k) // 个数不同
{cout << "test" << endl;
}

为什么C++支持重载,C语言不支持呢?

因为windows对函数重载的处理更加复杂,所以这里用linux下的gcc和g++来看更加直观。

首先我们要知道,链接器看到有函数被调用的时候,就会到符号表中去查找对应的函数名,来获取函数的地址,再链接到一起

先看C语言是怎么处理的
在这里插入图片描述
在这里插入图片描述
通过反汇编我们可以看到,C语言并没有对函数名进行处理,也就是说无论我们参数的个数,参数的类型,参数的顺序怎么修改,它只认函数名,如果出现了第二个相同函数名的,就算重定义。

下面再看C++的:
在这里插入图片描述
在这里插入图片描述
这里可以看到,C++对函数名进行了处理,函数以_Z4开头,接着是函数名,最后是所有参数的缩写。

_Z是所有函数的前缀,4是函数名的字符个数,例如第一个_Z4testii则代表函数名为test,具有四个字符,参数类型缩写分别是ii。

这也就是为什么返回值不同和参数名不构成重载的原因,它们不被作为对函数特征的处理。C++正是通过这种函数名修饰规则来实现函数的重载。


extern “C”

有时候我们在使用C++的时候,对于某些函数,想让它按照C的风格来编译,那么就在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。

在这里插入图片描述


引用再探

引用的特性

  1. 引用在定义的时候必须初始化(因为引用是某个对象的别名,所以必须初始化)
  2. 一个对象可以有多个引用
  3. 一旦引用一个实体,就不能再引用别的实体(有点类似指针的顶层const)

引用的使用场景

  1. 作为参数
struct A
{int arr[1000000];
};void test(A& s1)
{}

假设我们存在一个超级大的结构体,如果我们直接将结构体传过去的话,会产生一个临时变量来将这个结构体拷贝到形参中,这是极大的开销,但如果我们使用引用的话,传的只是一个别名而已,所有的操作还是在结构体本身上进行的,但是需要注意的和上面一样,如果我们要传递一个常量,就必须要在引用前加上const。

struct A
{int arr[1000000];
};void test (const A& s1)
{}
int main(int argc, char const *argv[])
{const A a = {10,324,32};test(a);return 0;
}
  1. 作为返回值
int& Add(int a, int b) 
{int c = a + b;return c;
}int main()
{int& ret = Add(1, 2);Add(3, 4);cout << ret << endl;return 0;
}

对于这样一个代码,我们可能第一眼觉得ret会是3。

在这里插入图片描述

但是其实是7。

因为我们返回的是c的一个引用,但是c只存在于调用时的那个栈帧,调用结束后那个栈帧就会被销毁,虽然销毁后数据不会被清空,但是那片区域的访问权限就会被放开,有可能会被下次调用的函数使用,也有可能会被其他的一个操作给使用,所以这是一种极为不安全的行为。

上面的7是第二次调用后修改了c的值。

所以,如果需要引用作为返回值,就必须保证出了函数作用域,返回的对象没有归还给系统,仍然存在。

以值作为参数或者返回值时,在传参和返回的时候,都会传递或返回原变量的一个临时的拷贝,这样的效率是非常低下的,尤其是数据特别大的时候,但如果使用引用作为参数的话,就不会有这样的问题。

引用和指针

语法概念上:引用是对象的一个别名,没有独立的空间,和其引用的实体共用一个空间。

但我们发现,引用其实和指针很像,它更像一个顶层const的指针,所以我们可以进入反汇编看看他们之间有没有关系

int main()
{int x = 5;int& y = x;int* z = &x;return 0;
}

在这里插入图片描述
反汇编下我们可以看到,指针和引用在汇编下的实现是一模一样的。

所以我们可以得出一个结论:引用是按照指针来实现的,在指针的基础上又给他封装了新的功能。

引用和指针的不同点:

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型 实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

内联函数

什么是内联函数?

用inline关键字修饰的函数就是内联函数,在编译时编译器会将函数的代码在调用内联函数的地方展开,减去了函数压栈的开销,提升程序运行的效率(牺牲空间换取时间)。
在这里插入图片描述
例如这样一个简单的代码
如果我们直接调用它
在这里插入图片描述
在汇编下可以看到,他会创建一个新的栈帧,将参数3,4压栈,然后计算完再返回结果

而如果在函数前面加上inline使其变为内联函数
在这里插入图片描述
这时再看,就会发现它直接把函数的代码在调用处直接展开,不会再创建新的栈帧。

内联函数的特性

  1. 内联函数是一种用空间换时间的做法,省去了创建栈帧和压栈的开销,但也因此代码很复杂和具有循环或递归之类的函数不适合作为内联函数,就算声明为内联函数编译器也会自动将其忽略。
  2. 内联函数不能声明和定义分离,因为一旦声明为内联函数,在调用的时候就会直接展开,没有了函数的地址,就无法将其链接到定义的部分。

值得一提的是,内联函数与C语言中的宏函数有些类似,虽然宏的性能不错,但是因为宏缺乏类型的安全检查和无法调试(在预处理阶段就进行了宏替换),在C++中宏函数被内联函数替代,宏常量定义被const取代。

内联函数的好处

  1. 较之等价的表达式更易于阅读
  2. 可以被其他应用重复利用,省去了重新编写的代价
  3. 如需修改计算过程,显然修改函数比先找到等价表达式所有出现的地方再逐一修改更容易。

类的内联成员函数的声明

我们可以在类内把 inline 作为声明的一部分显式地声明成员函数,同样的,也能在类的外部用 inline 关键字修饰函数的定义(当然在声明和定义的地方同时说明 inline 也是合法,只是没有必要)。

内联函数的使用

  • 滥用内联将导致程序变得更慢;
  • 最好不要内联超过 10 行的函数;
  • 谨慎对待析构函数,析构函数往往比其表面看起来要更长,因为有隐含的成员和基类析构函数被调用;
  • 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下,这些循环或 switch 语句从不被执行);
  • 有些函数即使声明为内联的也不一定会被编译器内联:比如虚函数和递归函数就不会被正常内联。
    • 通常,递归函数不应该声明成内联函数。(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的,大多数编译器都不支持内联递归函数)。
    • 虚函数内联的主要原因则是想把它的函数体放在类定义内,为了图个方便,亦或是当作文档描述其行为,比如精短的存取函数。

constexpr函数

概念

能用于常量表达式的函数

特征

  1. 函数的返回类型及所有形参的类型都得是字面值类型
  2. 函数体中必须有且只有一条return语句
  3. 编译器把对constexpr函数的调用替换成其结果值(constexpr函数被隐式地指定为内联函数)
  4. 函数体内允许包含 运行时不执行任何操作的语句
  5. 允许返回一个非常量,应用时编译器会进行检查。(constexpr不一定返回常量表达式)

内联函数和constexpr函数放在头文件内

和其他函数不同,内联函数和constexpr可以在程序中多次定义(每一次展开就是一次定义)。但多个定义必须完全一致,基于这个原因,内联函数和constexpr函数通常定义在头文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443826.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类的概念、成员函数的定义方式、类的访问控制和封装、类的大小、this指针

文章目录类的概念structclassclass和struct的区别是什么呢&#xff1f;类中成员函数的两种定义方式声明和定义都在类中声明和定义分离类的访问控制和封装类的封装特性类的大小结构体内存对齐规则类的存储方式this指针类的概念 在C中&#xff0c;类可以说是最重要的东西&#x…

jQuery实现两个列表框的值之间的互换:

jQuery实现两个列表框的值之间的互换&#xff1a; 截图如下&#xff1a; 代码如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath reque…

类的6个默认成员函数:构造函数、析构函数、拷贝构造函数、重载运算符、三/五法则

文章目录6个默认成员函数构造函数概念默认构造函数的类型默认实参概念默认实参的使用默认实参声明全局变量作为默认实参某些类不能依赖于编译器合成的默认构造函数第一个原因第二个原因第三个原因构造函数初始化构造函数里面的“”是初始化吗&#xff1f;为什么要使用列表初始化…

C++ 类的知识 | 构造函数再探、匿名对象、友元函数、内部类、类的const成员、类的static成员

文章目录构造函数再探以下代码共调用多少次拷贝构造函数委托构造函数概念形式匿名对象友元友元的声明友元类令成员函数作为友元函数重载和友元注意内部类特性类的const成员可变数据成员类的static成员概念关于static静态成员的类内初始化静态成员能用于某些普通成员不能的场景构…

顺序容器(vector、list、string、deque、forward_list)及迭代器、容器适配器

文章目录概述所有容器都支持的操作迭代器迭代器支持的操作迭代器支持的算术运算容器类型size_typeiterator 和 const_iterator容器定义和初始化拷贝初始化顺序容器独有的构造函数&#xff08;array除外&#xff09;array的初始化与内置数组类型的区别6种初始化方法&#xff08;…

jQuery实现表格隔行换颜色:

jQuery实现表格各行换颜色&#xff1a; 截图如下&#xff1a; 代码如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack处理中缀表达式【+、-、*、/、()】

文章目录题目描述思路使用getline()存储输入的字符串边读取边压栈完整代码题目描述 使用stack处理括号化的表达式。当你看到一个左括号&#xff0c;将其记录下来。当你在一个左括号之后看到一个右括号&#xff0c;从stack中pop对象&#xff0c;直至遇到左括号&#xff0c;将左括…

二维数组的查找

文章目录题目描述思路注意代码题目描述 在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该整数…

Springmvc,Spring MVC文件上传

Springmvc文件上传&#xff1a; 1.代码截图如下&#xff1a; 2.UploadController.java: package cn.csdn.controller;import java.io.File;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller; import org.springframework.ui.…

插入迭代器、流迭代器、反向迭代器、移动迭代器

文章目录前言插入迭代器inserterfront_inserterback_inserteriostream迭代器istream_iterator 读取输入流istream_iterator允许使用懒惰求值ostream_iterator操作反向迭代器reverse_iterator的base成员函数前言 除了为每个容器定义的迭代器之外&#xff0c;标准库在头文件iter…

泛型算法(lambda表达式、function类模板、bind函数适配器、迭代器类别、链表数据结构独有的算法)

文章目录概念find()函数迭代器令算法不依赖于容器但算法依赖于元素类型的操作算法永远不会执行容器的操作只读算法accumulate()函数从两个序列中读取元素&#xff08;equal函数为例&#xff09;迭代器作为参数形成两个序列equal()写容器元素的算法概念fill()fill_n()插入迭代器…

jsp,div 限制字数,超出部分用省略号代替

1.我是用struts2标签做的&#xff1a;如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% taglib prefix"s" uri"/struts-tags"%> <%String path request.getContext…

C++之关联容器

文章目录概述及类型mapsetpair类型概念初始化默认初始化提供初始化器允许的操作可以创建一个pair类的函数可以作为容器的类型关联容器迭代器概念map的迭代器set的迭代器是const的初始化map and setmultimap and multiset关联容器的操作额外的类型别名关联容器和算法删除元素添加…

动态内存、智能指针(shared_ptr、unique_ptr、weak_ptr)、动态数组

文章目录三种对象的分类三种内存的区别动态内存概念智能指针允许的操作智能指针的使用规范new概念内存耗尽/定位new初始化默认初始化直接初始化值初始化delete概念手动释放动态对象空悬指针shared_ptr类格式独有的操作make_shared函数shared_ptr的计数器通过new用普通指针初始化…

动态数组的简单应用

文章目录连接两个字符串字面常量题目注意代码输出结果处理输入的变长字符串题目注意代码连接两个字符串字面常量 题目 连接两个字符串字面常量&#xff0c;将结果保存在一个动态分配的char数组中。重写&#xff0c;连接两个标准库string对象。 注意 使用头文件cstring的str…

二分查找算法实现

文章目录思路代码以二分区间作为while判定条件以给定值作为while判定条件主函数思路 while判定条件的选择&#xff0c;注意最外层的return的内容就好。下面提供了两个代码版本。计算 mid 时需要防止溢出&#xff08;对应类型【如本例中的int】可能存不下&#xff09;&#xff…

根据中序、前序遍历重建二叉树

文章目录题目递归思路细节易错代码复杂度分析迭代思路细节易错代码复杂度分析题目 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 例如&#xff0c;给出 前序遍历 preorder [3,9,20,15,7] 中…

深搜+剪枝

文章目录题目思路注意代码复杂度分析题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c…

搜索+回溯问题(DFS\BFS详解)

文章目录题目思路DFS思路代码复杂度分析BFS思路代码复杂度分析题目 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&am…

快速幂实现pow函数(从二分和二进制两种角度理解快速幂)

文章目录迭代实现快速幂思路int的取值范围快速幂从二进制的角度来理解从二分法的角度来理解代码复杂度分析进阶——超级次方思路倒序快速幂正序快速幂代码复杂度分析迭代实现快速幂 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff0…