c++拾遗-----函数探幽
来源:互联网 发布:nestopia mac 手柄 编辑:程序博客网 时间:2024/06/12 22:12
1、内联函数
内联函数是c++为提高程序运行速度而做出的改进。常规函数域内联函数的区别在于:常规函数的调用使得程序跳到该函数的地址,并在函数结束时返回。而内联函数被编译器使用相应的代码替换函数的调用,程序无需跳到一个内存地址,在跳回来,速度稍快,但牺牲的是内存空间。同样
要使用这项特性,方法如下:
- 在函数声明前加上关键字inline;
- 在函数定义前加上关键字inline;
但实际上内联函数还要看编译器是否开放这种特性,在vs2010上可以将内联函数进行递归调用。而一般来说是不允许的。内联函数实际上是通过宏来实现的。
#include<iostream> inline int func(int n) { if (n == 0) return 1; else return n*func(n-1); }int main(){ using namespace std; cout<<func(5); system("pause"); return 0;}
2、引用变量
引用变量是以创建变量的别名。与以创建变量公用同一内存空间。
创建引用变量
int a=5;int &b=a;
通过指针可以更改指向内存的内容,但指针不必在声明时初始化,而且可以更改指向的地址。但引用变量必须在声明时初始化,且一旦与某个变量关联,就只能一直效忠它。所以引用更像指针常量。
函数参数传递
c++中参数传递方式有三种:
按值传递,这种方式会生成一个参数副本,函数内的操作并不会改变参数本身,当然数组除外,因为它传递的是数组的地址。
地址传递,如数组和指针,可以改变指针指向空间的内容。但其实同样传递后产生了一个指针副本,所以无法改变指针的指向的地址。
引用传递,传递的是变量的别名,与变量公用同一内存空间,所以可以直接改变参数的值。
临时变量,引用参数和const
当参数是const引用时,以下两种情况会生成临时变量:
- 实参类型正确,但不是左值。
- 实参类型不正确,但可以转换成正确的类型
#include<iostream>double refcube(const double &ra){ return ra*ra*ra;}int main(){ using namespace std; cout << refcube(7); system("pause"); return 0;}
传入值7,回生成临时变量,ra指向它,该变量只存在于函数调用期间。而把参参数列表中的const去除,则报错。
如果不是const引用,也生成临时变量,那么函数最终改变的对象就是临时变量,而不是希望的引用。所以其他情况无法生成临时变量
函数返回局部变量
函数返回一般局部变量是可以的,但如果返回的是局部变量的地址或引用时,调用会出错,因为指针指向的的内存空间在函数运行结束时被释放掉了。当然如果数据存储在静态存储区,那么该空间在程序结束时才会被释放。
#include<iostream>char * fun();char *fun1();int main(){ using namespace std; char* p = fun(); //数据当作常量存储在静态存储区,无法修改 //p[0] = 'c'; cout << p << endl; char *q = fun1(); cout << q << " "; q[0] = 'c'; cout << q << endl; system("pause"); return 0;}char * fun(){ char *p= "abcd"; return p;}char *fun1(){ static char p[5] = { "abcd" }; return p;}
何时使用引用参数
使用引用参数的原因主要有:
- 能够修改调用函数中的数据对象
- 通过传递引用而不是整个对象,可以提高程序的运行速度。
对于使用传递的值而不做修改的函数
- 如果数据对象很小,则使用按值传递
- 如果数据对象是数组,使用指针,并声明为常量指针
- 如果数据对象是较大的结构,则使用const指针或const引用。
- 如果数据对象是类对象,则使用const引用
对于修改调用函数中数据的函数
- 如果是内置对象,则使用指针
- 如果是数组,使用指针
- 如果是结构,指针或引用
- 如果是对象,则使用饮用
3、默认参数
默认参数指函数调用省略实参时自动使用默认值。
注意事项:
- 对于带参数列表的函数,必须从右往左添加,也就是说如果为某个参数设置默认值,则必须为他右边的所有参数设置默认值。
- 实参按从左到右依次付给相应的形参,不能跳过任何参数。
4、函数重载
函数重载主要针对于完成相同的工作,而使用不同的参数列表。c++允许定义定义名称相同的函数,前提是参数列表不同。返回值可以相同,但参数列表必须不同
double cube(double x);double cube(double &x);
上面两个函数不能共存,因为调用时无法确定使用哪个原型。
double cube(double x);double cube(const double x);
同上,这两个函数也无法共存。因为非const变量既可赋值给const变量,也可以赋值给非const变量。所以编译器无法确定使用哪个函数。
但当参数为引用或指针,则可以使用const进行重载,这是因为按值传递时,对用户而言,这是透明的,用户不知道函数对形参做了什么手脚,在这种情况下进行重载是没有意义的,所以规定不能重载!当指针或
引用被引入时,用户就会对函数的操作有了一定的了解,不再是透明的了,这时重载是有意义的,所以规定可以重载。
double cube(double &x);double cube(const double &x);double cube(double *x);double cube(const double *x);
如果重载的函数都是引用或指针,const 变量 只能调用带有const 参数的方法,非const 变量既能调用带const 参数的方法,也能调用不带cosnt 参数的方法,优先调用不带const 参数的方法.
5、函数模板
函数模板使用范型来定义函数,其中范型可以使用具体的类型来替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。
#include<iostream>template <typename T>void swap(T &a, T&b);int main(){ int a = 3, b = 5; std::cout << "a=" << a << ",b=" << b << std::endl; swap(a, b); std::cout << "a=" << a << ",b=" << b << std::endl; double c = 4.5, d = 5.5; std::cout << "c=" << c << ",d=" << d << std::endl; swap(c, d); std::cout << "c=" << c << ",d=" <<d << std::endl; system("pause"); return 0;}template <typename T>void swap(T &a, T &b){ a = a + b; b = a - b; a = a - b;}
要建立一个模板,关键字template和typename是必须的,其中typename可以用class替换。
重载的模板
需要对不同类型使用相同算法时,可以使用模板,但并不是所有类型都这样,所以可以想重载常规函数一样重载函数模板,记住,并非所有模板参数都必须是模板参数类型。
#include<iostream>template <typename T>void swap(T &a, T &b){ T temp; temp = a; a = b; b = temp;}template <typename T>void swap(T *a, T *b, int n){ T temp; for (int i = 0; i < n; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; }}int main(){ int a = 5,b=3; double c[5] = {1.2,2.3,3.4,4.5,5.6}; double d[5] = {6.7,7.8,8.9,9.1,10.2}; swap(a,b); std::cout << a <<" "<< b << std::endl; swap(c,d,5); for (int i = 0; i < 5; i++) std::cout << c[i] << " " << d[i] <<std::endl; system("pause"); return 0;}
显示具体化
因为对于某些特殊类型,可能不适合模板实现,需要重新定义实现,此时就是使用显示具体化的场景.
template <typename T>void swap(T &a,T&b);//template <>void swap(int &a,int &b);template <> void swap<int>(int &a,int &b);
上面两种写法皆可,显示具体化一定要有一个相同的模板函数,将其换成具体类型,所以需要一开始的模板函数。
显式实例化
代码中包含的模板并不会生成相应的定义,他们只是一个生成定义的方案,而显式实例化意味着无论程序是否是否使用,都会生成相应的定义。
template void swap<int>(int,int);
隐式实例化
后面有程序用了,编译器才会根据模板生成一个实例函数.
编译器选择使用哪个函数
1、进行完全匹配时,c++允许一些无关紧要的转换
2、重载解析过程将寻找最匹配的函数。如果只存在一个这样的函数,则选择该函数;如果存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;如果存在多个模板函数,但其中一个函数比其他的都具体,则选择该函数。当存在多个匹配的非模板函数或模板函数,或者不存在匹配的函数,都将出现错误。
3、通过代码选择
#include<iostream>template <typename T>T lesser(T t1, T t2){ return t1 > t2 ? t2 : t1;}int lesser(int a, int b){ a = a < 0 ? -a : a; b = b < 0 ? -b : b; return a < b ? a : b;}int main(){ using namespace std; int m = 20; int n = -30; double x = 15.5; double y = 19.5; cout << lesser(m, n)<<endl; cout << lesser(x, y) << endl; cout << lesser<>(m, n) << endl; cout << lesser<int>(x, y) << endl; system("pause"); return 0;}
第一个函数调用匹配非模板函数和模板函数,所以调用非模板函数。
第二个与非模板函数匹配
第三个<>指出需要使用模板函数
第四个指出要使用显示实例化函数,所以返回的是15而不是15.5
所以同时匹配模板函数和非模板函数时,默认使用非模板函数,但可以通过<>来强制使用模板函数
4、多个参数的函数
当函数有多个参数时,最匹配的函数一定是所有参数匹配程度都不比其他函数差
c++11新增关键字decltype
当在模板函数中无法知道要声明变量的类型时,可以使用关键字decltype来自动声明(有点像加强版的auto)。decltype确定变量类型的步骤如下:
假设有声明如下
decltype(expression) var;
1、如果expression是一个没有用括号扩起来的标识符,则var的类型与标识符的一致,包括const等限定符。
double x=5.5; double y=7.9; double &rx=x; const double *pd; decltype (x) w; //w is type double decltype (rx) u=y; //rx is type doubel & decltype (pd) v; //pd is const double *
2、如果expression是函数调用,则var的类型为函数返回类型。
3、如果expression是用括号扩起来的,则var指向其类型为引用
int y=5decltype((y)) z=y; //z type int &decltype(y) w=y; //w type int
4、如果上述条件都不满足,则var类型与expression一致
int j=3int &k=j;int &r=j;decltype (k+r) z;//z type int
decltype后置返回类型
在模板函数内不确定要声明变量类型时可以用decltype,但函数返回值无法直接使用decltype
template <typename T1,typename T2>auto swap(T1 t1,T2 t2) ->decltype(t1+t2){ return t1+t2;}
- c++拾遗-----函数探幽
- 【c++primer】函数探幽
- c++拾遗-----函数
- 【C语言】主函数的参数探幽
- 函数探幽
- 函数探幽
- 函数探幽
- C语言函数拾遗
- c++拾遗-----开始学习c++
- c++拾遗-----处理数据
- c++拾遗-----复合类型
- c++拾遗-----内存模型
- 转载:WIN32 SDK编程拾遗-系统托盘图标
- C++PrimerPlus第八章学习笔记——函数探幽
- 【学习笔记】C++primer plus 8.函数探幽
- 【C++】第8章 函数探幽 知识点总结
- 第八章 函数探幽
- 第八章 函数探幽
- WEB 前端学习
- -i++与i的值问题
- LeetCode Maximum Product Subarray
- SQLite 的错误码
- 如何在django里使用静态文件
- c++拾遗-----函数探幽
- Arduino 和 MQ-135 空气污染气体传感器 实验
- C++实验6—数组操作
- LeetCode7--Reverse Integer
- js回调函数(callback)
- 【杭电-oj】-2042-不容易系列之二(类似于猴子吃桃的爷爷和羊)
- 个人职业规划和面试技巧
- java位移经典
- java动态代理中的invoke方法是如何被自动调用的