函数探幽

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值