1、C++内联函数
内联函数:C++为提高程序运行速度所增加的功能。和常规函数相比,差别不在于编写方式,而在于编译器如何将它组合到程序代码中。
常规函数调用方式:
执行到函数调用指令时,程序在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到函数标记的内存单元,执行函数代码。调用完成后,跳转回地址被保存的位置,继续下面的主程序执行。
内联函数:
编译器将使用相应的程序代码,替换函数调用,对于内联代码,程序无需调到另外一个位置执行代码,再跳转回来。因为不需要来回跳转,内联函数的运行速度提高了,但是需要占用更多的内存。如果程序10个地方调用了同一个内联函数,那么该程序包含该函数代码的10个副本。而普通函数调用仅包含一个副本。
定义内联函数:
(1)在函数声明之前添加inline
(2)在函数定义之前添加inline
(3)函数声明和函数定义之前只需要一处添加inline即可
(4)内联函数申请编译器不一定会通过,比如内联函数过大,或者内联函数递归调用了。
#include <iostream>
inline double square(double x){
return x*x;
}
int main(int argc,char argv[]){
using namespace std;
double a,b;
duble c=13.0;
a = square(5.0);
b = square(4.5+7.5);
cout << a << " " << b << endl;
return 0;
}
内联与宏定义差别:
(1)宏定义是文本替换,是不能传递参数的,内联是代码复制。
(2)宏定义不能按值传递。而inline比宏优势在于按值传递。比如在写递归计算的时候,内联比宏定义合适。
2、引用变量
引用:不是一个新的定义变量,是已经存在变量的一个别名。
引用变量:主要用途是用作函数参数,通过将引用变量作为参数,函数将使用变量的原始数据,而不是一个副本。
创建引用变量:
(1)必须在声明引用变量的时候将其初始化,而不能先声明,在赋值
(2)一个引用变量初始化完成之后,不能再更改,一直效忠。
#include <iostream>
int main(int argc,char** argv){
using namespace std;
int rats = 101;
//定义一个引用变量rodents,引用变量rats
int & rodents = rats;
//rats的值应该等于rodents的值
cout << "rats = " << rats << ",rodents=" << rodents << endl;
//因为rodents只是rats的一个引用,不是副本,所以内存地址也一样
cout << "&rats = " << &rats << ",&rodents=" << &rodents << endl;
int bunnies = 50;
//rodents是一个引用,因为引用一旦初始化了,不可更改,此处不是修改rodents为bunnies的引用,而是赋值bunnies给rodents内存,也就是说,此操作后rats,rodents,bunnies值都是相等的。
rodents = bunnies;
//bunnies,rats,rodents值相等
cout << "bunnies=" << bunnies << ",rats=" << rats << ",rodents=" << rodents << endl;
//rats和rodents内存地址一样,bunnies内存地址和前者不一样
cout << "&bunnies=" << &bunnies << ",&rats=" << &rats << ",&rodents=" << &rodents << endl;
return 0;
}
(3)将引用作为函数参数传递
引用作为函数参数传递时,实际传递的不是参数的一个副本,而是参数本身,此时修改实参和修改形参效果一样。在C语言中,要对传入参数进行修改,则必须使用指针。
#include <iostream>
void swapr(int &a,int &b); // 按引用传递函数参数
void swapp(int *p,int *q); // 按指针传递函数参数
void swapv(int a,int b); // 按值传递函数参数
int main(int argc , char** argv){
using namespace std;
int wallet1=300;
int wallet2=350;
//输出"wallet1=300,wallet2=350"
cout << "wallet1=" << wallet1 << ",wallet2=" << wallet2 << endl;
swapr(wallet1,wallet2);
//输出"wallet1=350,wallet2=300",实际已经发生交换
cout << "wallet1=" << wallet1 << ",wallet2=" << wallet2 << endl;
swapp(&wallet1,&wallet2);
//输出"wallet1=300,wallet2=350",实际已经发生交换
cout << "wallet1=" << wallet1 << ",wallet2=" << wallet2 << endl;
swapv(wallet1,wallet2);
//输出"wallet1=300,wallet2=350",实际没有发生交换
cout << "wallet1=" << wallet1 << ",wallet2=" << wallet2 << endl;
return 0;
}
void swpar(int &a,int &b){
int temp;
temp=a;
a=b;
b=temp;
}
void swapp(int *p,int *q){
int temp;
temp=*p;
*p=*q;
*q=temp;
}
void swapv(int a,int b){
int temp;
temp=a;
a=b;
b=temp;
}
(4)将引用用于结构体数据类型:
#include <iostream>
#include <string>
struct free_throws{
std::string name;
int made;
int attempts;
float percent;
};
void display(const free_throw & ft);
void set_pc(free_throw &ft);
free_throw & accumulate(free_throws & target,const free_throw& source);
int main(int argc , char argv[]){
//partial initializetons - remaining member set to 0
free_throws one = {"Ifelse Branch" , 13 , 14};
free_throws two = {"Andor Knott", 10 , 16 };
free_throws three = {"Minnie Max", 7 , 9 };
free_throws four = {"Whily Looper", 5 , 9 };
free_throws five = {"Long Long", 6 , 14 };
free_throws team = {"Throwgoods", 0 , 0 };
//no initialization
free_throws dup;
set_pc(one);
display(one);
accumulate(team,one);
display(team);
//use return value as argument
display(accumulate(team,two));
accumulate(accumulate(team,three),four);
display(team);
//use return value in assignment
dup = accumulate(team,five);
std::cout << "Displaying team:\n";
display(team);
std::cout << "Displaying dup after assignment " << endl;
display(dup);
set_pc(for);
//ill - advised assignment
accumulate(dup,five)=four;
std::cout << "Displaying dup after ill-advised assignment :\n";
display(dup);
return 0;
}
// display 功能不需要对数据进行修改,因此可以使用按值传递,但是按引用传递,并且使用const修饰,保证对传入的值不修改,能够节省内存,提高效率
void display(const free_throws &ft){
using std::cout;
cout << "Name:" << ft.name << endl;
cout << "Made:" << ft.made << endl;
cout << "Attempts:" << ft.attempts << endl;
cout << "percent: " << ft.percent << endl;
}
//因为要对传入的参数值进行修改,因此可以使用按引用传参和按指针传参,但是使用引用传参节省内存
void set_pc(three_throws &ft){
if(ft.attempts != 0){
ft.percent = 100.0f * float(ft.made) / float(ft.attempts);
}else{
ft.percent = 0;
}
}
//要对第一个参数进行修改,因此使用按引用传参,不对第二个参数修改,因此使用const引用传参
//以引用的方式作为返回值,这样返回值的地址和参数target的内存地址相同的。
free_throw & accumulate(free_throws & target , const free_throws & source){
target.attempts += source.attempts;
target.made += source.made;
set_pc(target);
return target;
}
(5)返回引用和普通返回值
普通返回值:先重新生成一个数据,然后将要返回的值复制到该内存。
引用返回:返回引用对象的一个别名。但是要避免返回一个临时变量的引用,避免返回指向临时变量的指针。一般会返回一个引用参数。
//以下是三个string字符串拼接的实现
//第参数s1和s2采用引用,然后返回临时值得拷贝,这样没有问题。
string version1(const string & s1,const string & s2){
string temp;
temp = s2 + s1 + s2;
return temp;
}
//返回一个引用参数的引用,原理上不会有什么问题,但是这样产生负面影响就是会修改s1
const string & version2(string & s1,const string & s2){
s1 = s2 + s1 + s2;
return s1;
}
//返回一个临时值的引用,运行出错,该方法完全行不通
const string & version3(string & s1,const string & s2){
string temp ;
temp = s2 + s1 + s2;
return temp;
}
3、默认参数
默认参数:在函数调用中传递参数的时候,如果省略了一些参数传递,则这些参数使用一个默认值。
下面就是一个使用默认参数的函数原型:
//当调用该函数时,如果没有传递参数 n ,也就是只有一个参数str,则n使用默认值 n = 1
char* left(const char * str,int n = 1);
注意事项:
(1)要对某个参数设置默认参数,则其右边的参数都应该设置成默认参数。
(2)实参按照从左到右的顺序依次赋给相应的参数,不能跳过任何参数。
(3)使用默认参数的赋值规则:当实参个数少于形参个数时,优先从左至右顺序给形参赋值,当参数不够时,该参数以及其右边的参数都使用默认参数。
#include <iostream>
const int ArSize = 80;
char * left(const char* str,int n = 1);
int main(int argc , char argv[]){
using namespace std;
char sample[ArSize];
cout << "Enter a string:\n";
cin.get(sample,ArSize);
char * ps = left(sample , 4); //使用实际传入的值,n = 4
cout << ps << endl;
delete [] ps;
ps = left(sample); //使用默认参数值,n = 1
cout << ps << endl;
delete [] ps;
return 0;
}
char * left(const char* str,int n){
if(n<0){
n=0;
}
char *p = new char[n+1];
int i;
for( i = 0; i< n && str[i] ;i++){
p[i] = str[i];
}
while(i<=n){
p[i++] = '\0';
}
return 0;
}
4、函数重载
函数重载意义:
使用相同的函数名,完成相同的任务,但是使用不同的参数列表。比如计算两个数的和,定义函数add,可以重载add函数,使其可以计算float类型,也可以计算int类型。而不用定义很多个函数名。
函数重载关键:
必须是函数的特征标不同,也就是函数参数列表不同。
(1)编译器在检查函数特征标的时候,将引用类型和类型本身视为相同的。
double cube(double x);
double cube(double & x);
(2)匹配函数时并不区分const和非const。但是将非const赋值给const是合法的,将const赋值给非const是非法的。
void dribble(char* bits); //overloaded
void dribble(const char cbits); //overloaded
void dabble(char * bits); //not overloaded
void dribel(const char * bits); //not overloaded
const char p1[20]="How is the weather?";
char p2[20]="How is business?";
dribble(p1); //dribble(const char cbits)
dribble(p2); //dribble(char* bits)
dabble(p1); //not match
dabble(p2); //dabble(char * bits)
drivel(p1); //dribel(const char * bits)
drivel(p2); //dribel(const char * bits)
(3)特征标仅仅是参数列表,不包含返回值。
long gunk(double a,int b); //same signatures
int gunk(double a,int b); //not allowed
(4)对于普通类型参数只有const、volatile区别不能视为重载,但是如果参数是指针或 引用类型,那么const、volatile的区别视为重载。
(5)是否为常函数不能视为重载。
函数重载实例:
#include <iostream.h>
int abs(int a); //当参数为整型数据时的函数原型
float abs(float a); //当参数为浮点型数据时的函数原型
double abs(double a); //当参数为双精度型数据时的函数原型
int main(){
int a=-5,b=3;
float c=-2.4f,d=8.4f;
double e=-3e-9,f=3e6;
cout <<"a=" <<abs(a) <<endl <<"b=" <<abs(b) <<endl;//输出函数返回的结果
cout <<"c=" <<abs(c) <<endl <<"d=" <<abs(d) <<endl;
cout <<"e=" <<abs(e) <<endl <<"f=" <<abs(f) <<endl;
return 0;
}
int abs(int a)//函数定义
{
cout <<"int abs" <<endl;//显示运行了哪个函数
return (a>=0?a:-a);//如果a大于等于零则返回a,否则返回-a。
}
float abs(float a)
{
cout <<"float abs" <<endl;
return (a>=0?a:-a);
}
double abs(double a)
{
cout <<"double abs" <<endl;
return (a>=0?a:-a);
}