河北婚庆网站建设定制,付费下载 wordpress,广东网站备案电话号码,网站普查建设背景文章目录函数重载为什么C支持重载#xff0c;C语言不支持呢#xff1f;extern “C”引用再探引用的特性引用的使用场景引用和指针引用和指针的不同点:内联函数什么是内联函数#xff1f;内联函数的特性内联函数的好处类的内联成员函数的声明内联函数的使用constexpr函数概念…
文章目录函数重载为什么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语言规则来编译。 引用再探
引用的特性
引用在定义的时候必须初始化因为引用是某个对象的别名所以必须初始化一个对象可以有多个引用一旦引用一个实体就不能再引用别的实体有点类似指针的顶层const
引用的使用场景
作为参数
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;
}作为返回值
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;
}反汇编下我们可以看到指针和引用在汇编下的实现是一模一样的。
所以我们可以得出一个结论引用是按照指针来实现的在指针的基础上又给他封装了新的功能。
引用和指针的不同点:
引用在定义时必须初始化指针没有要求引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型 实体没有NULL引用但有NULL指针在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占 4个字节)引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小有多级指针但是没有多级引用访问实体方式不同指针需要显式解引用引用编译器自己处理引用比指针使用起来相对更安全 内联函数
什么是内联函数
用inline关键字修饰的函数就是内联函数在编译时编译器会将函数的代码在调用内联函数的地方展开减去了函数压栈的开销提升程序运行的效率牺牲空间换取时间。 例如这样一个简单的代码 如果我们直接调用它 在汇编下可以看到他会创建一个新的栈帧将参数3,4压栈然后计算完再返回结果
而如果在函数前面加上inline使其变为内联函数 这时再看就会发现它直接把函数的代码在调用处直接展开不会再创建新的栈帧。
内联函数的特性
内联函数是一种用空间换时间的做法省去了创建栈帧和压栈的开销但也因此代码很复杂和具有循环或递归之类的函数不适合作为内联函数就算声明为内联函数编译器也会自动将其忽略。内联函数不能声明和定义分离因为一旦声明为内联函数在调用的时候就会直接展开没有了函数的地址就无法将其链接到定义的部分。
值得一提的是内联函数与C语言中的宏函数有些类似虽然宏的性能不错但是因为宏缺乏类型的安全检查和无法调试在预处理阶段就进行了宏替换在C中宏函数被内联函数替代宏常量定义被const取代。
内联函数的好处
较之等价的表达式更易于阅读可以被其他应用重复利用省去了重新编写的代价如需修改计算过程显然修改函数比先找到等价表达式所有出现的地方再逐一修改更容易。
类的内联成员函数的声明
我们可以在类内把 inline 作为声明的一部分显式地声明成员函数同样的也能在类的外部用 inline 关键字修饰函数的定义当然在声明和定义的地方同时说明 inline 也是合法只是没有必要。
内联函数的使用
滥用内联将导致程序变得更慢最好不要内联超过 10 行的函数谨慎对待析构函数析构函数往往比其表面看起来要更长因为有隐含的成员和基类析构函数被调用内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下这些循环或 switch 语句从不被执行)有些函数即使声明为内联的也不一定会被编译器内联比如虚函数和递归函数就不会被正常内联。 通常递归函数不应该声明成内联函数。递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的大多数编译器都不支持内联递归函数)。虚函数内联的主要原因则是想把它的函数体放在类定义内为了图个方便亦或是当作文档描述其行为比如精短的存取函数。 constexpr函数
概念
能用于常量表达式的函数
特征
函数的返回类型及所有形参的类型都得是字面值类型函数体中必须有且只有一条return语句编译器把对constexpr函数的调用替换成其结果值constexpr函数被隐式地指定为内联函数函数体内允许包含 运行时不执行任何操作的语句允许返回一个非常量应用时编译器会进行检查。constexpr不一定返回常量表达式
内联函数和constexpr函数放在头文件内
和其他函数不同内联函数和constexpr可以在程序中多次定义每一次展开就是一次定义。但多个定义必须完全一致基于这个原因内联函数和constexpr函数通常定义在头文件。