引用说明:
内联函数:【C++】 内联函数详解(搞清内联的本质及用法)_c++内联函数-CSDN博客
函数重载:《C++初阶之路》函数重载-CSDN博客
一、Class申明
说明:以复数为例
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i) //构造函数
{ }
complex& operator += (const complex&); //函数在外部定义
double real () const { return re; ) //函数在内部定义
double imag () const { return im; ) //函数在内部定义
private:
double re, im;
friend complex& _doap1 (complex*, const complex&);
};
二、class 实例化
{
complex c1 (2,1);
complex c2;
}
三、内联函数(inline)
1.为什么使用内联函数
- 内联函数最初的目的:代替部分 #define 宏定义;
1.宏是预处理指令,在预处理的时候把所有的宏名用宏体来替换;内联函数是函数,在编译阶段把所有调用内联函数的地方把内联函数插入;
2.宏没有类型检查,无论对还是错都是直接替换;而内联函数在编译时进行安全检查;
3.宏的编写有很多限制,例如只能写一行,不能使用return控制流程等;
4.对于C++ 而言,使用宏代码还有另一种缺点:无法操作类的私有数据成员。
- 使用内联函数替代普通函数的目的:提高程序的运行效率;
函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码。CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码;被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行。
一个 C/C++程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是main(),终点也是main()。当main()调用完了所有的函数,它会返回一个值(例如return 0;)来结束自己的生命,从而结束整个程序。
函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
栈空间就是指放置程式的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足所造成的程式出错的问题,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。
为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function)。但也存在缺点,就是每一调用处均会展开,增加了重复的代码量。
2.内联函数和编译过程的相爱相杀
- 函数前面加上inline一定会有效果吗?
答:不会,使用内联inline关键字修饰函数只是一种提示,编译器不一定认。
- 如果不加inline就不是内联函数了吗?
答:存在隐式内联,不用inline关键字,C++中在类内定义的所有函数都自动称为内联函数。
- 内联函数一定就会展开吗?
答:其实和第一个问题类似,还是看编译器认不认。
- 在什么情况下内联函数会展开?
答:首先需要满足有inline修饰或者是类中的定义的函数,然后再由编译器决定。
3.内联函数怎么用,在哪儿用
隐式内联
C++中在类内定义的所有函数都自动称为内联函数,类的成员函数的定义直接写在类的声明中时,不需要inline关键字
#include <stdio.h> class Trace{ public: Trace() { noisy = 0; } void print(char *s) { if (noisy) { printf("%s", s); } } void on(){ noisy = 1; } void off(){ noisy = 0; } private: int noisy; };
显式内联
需要使用inline关键字
#include <stdio.h> class Trace{ public: Trace() { noisy = 0; } void print(char *s); //类内没有显示声明 void on(){ noisy = 1; } void off(){ noisy = 0; } private: int noisy; }; //类外显示定义 inline void Trace::print(char *s) { if (noisy) { printf("%s", s); } }
四、访问级别
只能访问public中的,private中的变量要通过调用函数获取
{ complex c1(2,1); cout << c1.real(); cout << c1.imag(); }
五、构造函数
complex (double r = 0, double i = 0) //(默认参数)
: re (r), im (i) //(初值列)
注意:写成下述,降低运行效率
complex (double r = 0, double i = 0) {re =r;im=i;}
六、函数重载
1.什么是函数重载?
C++允许同一作用域中出现函数名相同,参数不同,功能相似的函数,而这些函数就构成函数重载。
void Swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } void Swap(double* a, double* b) { double temp = *a; *a = *b; *b = temp; } //这两个函数就构成函数重载
2.函数重载的条件
第一要满足:函数名相同
第二要满足:参数不同,具体表现在参数类型的顺序、参数的个数、参数的类型。
参数的个数不同的函数重载
int Add(int a, int b, int c) { return a + b + c; } int Add(int a, int b) { return a + b; } //个数不同
参数类型的顺序不同
void Func(int a, double b) { cout << a << b << endl; } void Func(double a, int b) { cout << a << b << endl; } //顺序不同
3.为什么有函数重载
就是问函数重载到底能干嘛?c语言不支持同一作用域存在同名函数,那么我们修改一下函数名不就行了吗?函数重载真正能体现价值的地方到底在哪呢?
- 第一:书写函数名方便。
比如,我们要写两个交换函数,第一个是交换两个整形,第二个是交换两个浮点型,那么你只需要都取Swap即可,不需要写成Swapi、Swapd,如果参数复杂呢?你又要写成什么呢?
到不如直接写成Swap,然后传不同的参数,编译器会自动匹配最符合的函数。
- 第二:类中构造函数的实现也依靠函数重载
构造函数是同名的成员函数,它一般被划分为:有参构造、无参构造、拷贝构造,它们构成函数重载。
- 第三:模板的底层实现也依靠函数重载
模板的其实就是让编译器就为你创建重载函数,比如Swap(const T& a,const T&b)
当你传两个整形给a和b时,编译器会自动生成Swap(int* a,int* b),当你传两个浮点型double时,,编译器会自动生成Swap(double* a,double* b)。
以上等等C++中好用的机制都依靠于函数重载,函数重载在C++中的地位不言而喻。
4. 重载和重写的区别
1,重载是用来描述同名函数具有相同或者相似功能,但是参数个数或者类型顺序不一样的函数管理操作
返回值不能作为重载的条件
2,重写是子类对父类同名函数的重新定义
二者区别:
1,作用域不同:重载是在同一区域,子类无法重载父类,父类同名函数的将被覆盖,重写是在父类与子类之间
2,重载是在编译期间根据参数列表决定,重写是在运行期间根据具体对象类型决定调用函数