C++ Primer Plus 第九章

函数的执行需要一定的开销,C++中,内联函数在编译时被相应的函数代码替换函数调用,因此,内联函数被常规函数快,但代价是需要占用更多的内存(提高速度有限,除非函数调用次数很多)

要使用内联,必须采取下述措施之一
1 在函数声明前加上关键字 inline
2 在函数定义前加上关键字 inline
通常的做法是省略原型,将函数实现放在原型处

内联与宏
inline double square(double x)(return x*x);          //正确
#define SQUARE(X) X*X                                   //SQUARE(1 + 2) --> 1 + 2 * 1 + 2 错误!
#define SQUARE(X) (X)*(X)                              //SQUARE(C++)   --> (c++)*(c++)     c 被递增两次,与原意不符,错误!

引用
int rats;
int &dodents = rats;
必须在声明引用时将其初始化,而不能向指针那样,先声明,再初始化

引用更接近 const 指针
int &rodents = rats;
int * const pr = &rats;
具体区别呢?--> 引用更接近是一种语法糖

void swapr(int &a, int &b);  // swapr(i, j);
void swapp(int *p, int *q);  // swapp(&i, &j);

如果实参与引用参数不匹配,C++有时将生成临时变量(现在,当参数为const时,C++才允许这样做,
这是因为const变量不能修改,所以生成一个临时变量去使用也可以,否则,生成的临时变量可以改变,原参数却不会改变,就可能引起错误)
double refcube(const double &ra);
double side = 3.0;
double *pd  = &side;
double &rd = side;
long edge = 5L;
double lens[4] = {2.0, 5.0, 10.0, 12.0}
double c1 = refcube(side);               //ra 就是 ride
double c2 = refcube(lens[2]);          //ra 就是 lens[2]
double c3 = refcube(rd);               //ra 就是 rd 就是 side
double c4 = refcube(*pd);               //ra 就是 *pd 就是 side
double c5 = refcube(edge);               //ra 是一个临时变量
double c6 = refcube(7.0);               //ra 是一个临时变量
double c7 = refcube(side + 7.0);     //ra 是一个临时变量
例如
void swapr(int &a, int &b){...}
long a = 3, b= 5;
swapr(a, b);     //编译器将创建两个临时int变量,将它们初始化为3,5, 然后交换临时变量的值,a,b 的值并不会改变,在VS中,会直接编译出错

通常将结构或类的返回引用声明为const是最好的选择:其并不意味着类本身为const,而只意味着不能使用返回的引用来直接修改它指向的结构(即引用为const)
const sysop& use(sysop &sysopref)
{
     //....改变sysopref的值
     return sysopref;
}
sysop looper;
sysop copycat = use(looper);     //这里的copycat与looper并不是同一个,因为返回时发生了拷贝,如果不将返回引用声明为const,那么
use(looper).id = 1;                    //这里改变了looper的值,因为这里还没有发生拷贝,为了避免这种歧义,应该将返回引用声明为const(不同情况要不同对待,如cin的返回值)

如果函数返回一个结构或者类,该类将会复杂到一个临时返回存储单元中,然后复制到调用接受方的内存中,然而,如果返回一个结构或者类的引用,那么类的内容将直接复制到接受方的内存中。
但是,这样做要注意肯能会返回函数内部的临时变量,如果返回的是临时变量的引用,那么调用方如果使用了该引用,则会出 引用已释放内存的 错误!

设置默认参数,必须通过函数原型(函数实现时不加),且必须从右向左添加默认值,即,要为某个参数设置默认值,则不惜为它右边的所有参数提供默认值
int harpo(int n, int m, int j = 5);
int groucho(int n, int m = 1, int j = 1);
int choco(int n, int m = 1, int j);          //错误!

函数重叠(多态的一种)->两个函数的参数数目/参数类型不同,则函数可以同名

假设
void print(double d, int wdith);     #1
void print(int d, int wdith);          #2
void print(long d, int wdith);          #3

unsigned int year = 3210;
print(year, 6);
那么
如果#1,#2,#3只存在一个,那么C++将尝试强制转换,但如果3个都存在,则由于C++没有转换优先级,故C++会报错

匹配函数时,并不区分const和非const变量(?)
void dribble(char *bits);               #1
void dribble(const char *bits);          #2     //这是重载的
调用时,根据调用方是否为const char*确定使用方法
但是,如果只有#2,其也接收非const的char*,如果只有#1,则无法接收const char*

C++如何跟踪每一个重叠函数?--> 名称修饰,即对于编译器来说,不同的同名重载函数,其实是取了不同名字的

函数模板
template<class Any>
void swap(Any &a, Any &b){
     Any temp;
     temp = a;
     a = b;
     b = temp;
}
//用模板,关键字template和clss是必须的,类型名(这里是Any)可以自己取,如T,也可以用typename替换clss,如
template<typename T>
void swap(T &a, T &b){
     T temp;
     temp = a;
     a = b;
     b = temp;
}
PS:函数模板不能缩短执行程序,使用了多少种模板,其实就实际生成了多少对应的函数

重载模板
template<typename T>
void swap(T &a, T &b){}

template<typename T>
void swap(T a[], T  b[], int n){}     //这里的第三个参数为n,表明模板中的参数类型也可以是普通类型

显式具体化
1 第三代具体化(当前标准)
(1)对于给定的函数,可以有非模板函数、模板函数、显式具体化模板函数以及它们的重载版本
(2)显示具体化原型和定义应以template<>打头,并通过名称来指出类型
(3)优先级:非模板函数 覆盖 具体化模板函数 覆盖 常规模板函数

//普通模板
template<typename T>
void Swap(T &a, T&b)
//Job类的显式具体化
template<>
void Swap<Job>(job &a, job &b)

实例化,模板本身并非函数定义,只有当编译器为特定类型生成函数定义时,才会得到模板的实例(instantiation)
使用如下语法显示实例化模板(隐式实例化是指编译器自动得到的实例)
template
void  Swap<Job>(job &a, job &b) //与显式具体化不同 没有template后面没有<>  ... (1 可以将显式实例化的内容放在.cpp中,否则只能放在.h中)

试图同时使用同一种类型的显式实例化与显式具体化会出 错误!

编译器对函数版本的选择:
对于函数重载,函数模板,函数模板重载,C++用 重载解析(overlaoding resolution)。
第一步:创建候选函数列表:
     其中包含与被调用函数的名称相同的函数和模板函数
第二步:使用候选函数列表创建可行函数列表:
     这些都是参数数目正确的函数,为此有一个隐式转换序列。例如:使用float的参数的函数调用可以将该参数转换为double,从而接受double类型的参数,而模板可以生成一个double类型的实例
第三部:确定是否有最佳函数:
     如果有,则使用它;否则,出错。
    
ex:
may('B') //调用
第一步:找到如下候选函数列表:
     void may(int);                                             #1
     float may(float, float = 3);                         #2
     void may(char);                                             #3
     char *may(const char *);                              #4
     char may(const char &);                                   #5
     template<typename T>void may(const T &);          #6
     template<typename T>void may(T *)                    #7
第二步:筛选:
     #4与#7不合格,因为int型不能隐式转换为指针类型
第三步:确定最佳:按以下顺序进行:
     1 完全匹配(但常规由于模板)                              #3 #5 #6
     2 提示转换(例如:char->int, float->double)          #1
     3 标准转换(例如:int->char, long->double)          #2
     4 用户定义的转换
如果有多个完全匹配,且找不出最佳匹配,则会出错!

完全匹配允许 无关紧要 的转换(如 &, [] <-> * , const, volatile)
完全匹配的最优->
1 形参const与非const是完全匹配的,但可以用来区分非const的实参(优先调用非const版本的函数,只对指针与引用有效)
2 非模板函数  > 具体化函数 > 普通模板函数

一个比较坑的例子
template<typename T> void recycle(Type t);     #1
template<typename T> void recycle(Type *t);     #2
blot link = {...}
recycle(&link);
其与#1匹配,T被解释为 blot* --> recycle<blot *>(blot *);
也与#2匹配,T被解释为blot   --> recycle<blot>(blot *);]
此例中#2更具体,优先级更高,因为其需要的转换更少??

template<typename T>
void ShowArray(T arr[], int n);     //#1

template<typename T>
void ShowArray(T* arr[], int n); //#2

int tings[6] = {...};
double *pd[3];

ShowArray(things);          #1
ShowArray(pd);               #2
如果没有#2,则
ShowArray(pd);               #1 //但是这里可能就会出错,因为传递其实是地址

有多个参数的函数:其所有参数匹配度都必须不必其他函数差,并且有一个参数匹配程度比其他函数高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值