Day37、函数重载、缺省参数、哑元参数、内联函数inline、内存分配、引用

来源:互联网 发布:网络点播系统 编辑:程序博客网 时间:2024/05/29 11:00

一、            C++函数

1、 函数重载

例:图形库画图函数

C:

画圆形:drawCircle(int x,int y,double r){……}

画矩形:drawRect(int x,int y,double w, double h){….}

C++:

画圆形:draw (int x,int y,double r){……}

画矩形:draw (int x,int y,double w, double h){….}

函数相同,参数不同

1) 定义

在相同的作用域,定义同名的函数,但是它们的参数表必须有所区分,这样函数构造重载关系。

注:函数重载和返回类型无关,与参数名无关

2) 函数重载匹配

调用重载关系的函数时,编译器将根据实参与形参的匹配程度,自动选择最优的匹配版本

例:

  1 #include<iostream>

  2 using namespace std;

  3 int foo(int a){

  4    cout<<"foo(int)"<<endl;

  5 }

  6 void foo(int a,int b){

  7    cout<<"foo(int,int)"<<endl;

  8 }

  9 void foo(void){

 10    cout<<"foo(void)"<<endl;

 11 }

 12 //void foo(int b,int a){ }  无法重载

 13 void foo(int a,float b){

 14    cout<<"foo(int ,float)"<<endl;

 15 }

 16 int foo(float b,int a){

 17    cout<<"foo(float,int)"<<endl;

 18 }

 19 int main(){

 20     foo();

 21    foo(10);

 22    foo(10,20);

 23    // foo(10,3.14); 3.14是double类型,小数默认为双精度

24     foo(10,3.14f); //匹配foo(int,float)

 25    /* 

 26      一个函数指针调用重载函数,由函数指针类型决定,而不由实参决定

 27     */

 28    void (*pfunc)(int,float)=foo;//函数指针

 29    pfunc(10,20)

 30    return 0;

 31 }

foo(void)

foo(int)

foo(int,int)

foo(int ,float)

对于g++ v4.4.7编译器的一般匹配规则:

完全匹配>常量匹配>升级转换>降级转换>省略号匹配

  1 #include<iostream>

  2 using namespace std;

  3 //升级转换:char--->int

  4 void bar(int i){

  5    cout<<"bar(1)"<<endl;

  6 }

  7 //常量转换:char-->const char

  8 void bar(const char c){

  9    cout<<"bar(2)"<<endl;

 10 }

 11 //降级转换:short-->char    不安全

 12 void func(char c){

 13    cout<<"func(1)"<<endl;

 14 }

 15 //升级转换:short-->int    安全

 16 void func(int i){

 17    cout<<"func(2)"<<endl;

 18 }

 19 //过分的升级转换short-->long long

 20 void func(long long ll){

 21    cout<<"func(3)"<<endl;

 22 }

 23 //不定长参数:可以接受任意多个任意长度的参数,类似printf

24 //省略号匹配(最差)

 25 void hum(int i,...){

 26    cout<<"hum(1)"<<endl;

 27 }

 28 //降级转换:double-->int

 29 void hum(int i,int j){

 30    cout<<"hum(2)"<<endl;

 31 }

 32 int main(){

 33    char c='A';

 34    bar(c);//bar(2)

 35    short s;//一般情况下,升级转换的优先级高于降级优先级

 36    func(s);//调用func(2)

 37    hum(2,3.14);//降级转换优于省略号匹配,调用hum(2)

 38    return 0;

 39 }

3)函数重载的原理

C++编译器是通过对函数进行换名,将参数表的信息整合到函数名中,实现函数重载与名字冲突的矛盾。

例:

int add(int,int){}

intadd(double,int){}

tarena@tarena-virtual-machine:~$g++ -c  3.c

tarena@tarena-virtual-machine:~$nm 3.o

00000005 T_Z3adddi

00000000 T_Z3addii

笔试题:extern “C”的作用

函数声明前加入extern “C” 要求C++编译器不对函数做换名,便于C程序调用该函数

注:extern ”C”修饰的函数无法重载

extern “C” voidfunc(){ …. }

extern “C” {

       void func1(){….}

       void func2(){….}

}

2、 函数的缺省参数

1) 可以为函数的部分形参或全部形参指定缺省值,调用该函数时,如果不给实参,就取去缺省值作为相应的形参的值

void func(inta,int b=10 ){

cout<<a+b<<endl;

}

int main(void){

        func(10) ; // 20

        func(10,20); //30

}

2)缺省参数必须靠右侧,如果一个参数有缺省值,那么这个参数右侧所有的参数都必须带有缺省值

void func(inta=10,in b){  //error

     cout<<a+b<<endl ;

}

例:

 1 #include<iostream>

  2 using namespace std;

  3 void foo(int a,int b=200,int c=100){

  4    cout<<"a="<<a<<endl;

  5    cout<<"b="<<b<<endl;

  6    cout<<"c="<<c<<endl;

  7 }

  8 void foo(int i){}

  9 int main(void){

 10    foo(10,20,30);// 10,20,30

 11    foo(10,20);//10,20,100

 12    // foo(10);  注意歧义有错误

 13    return 0;

 14 }

3)如果函数的定义和声明分开,缺省函数应该写在函数声明部分,而定义部分不写但可以加注释。

Void func(int a,int b/*=100*/){ … } ;  函数定义

Void func(int a,int b=100) ;  函数声明

例:

  1#include<iostream>

  2using namespace std;

  3void foo(int a,int b=200,int c=100);//函数声明

  4void foo(int i){}

  5int main(void){

 6     foo(10,20,30);// 10,20,30

 7     foo(10,20);//10,20,100

 8     // foo(10);  注意歧义有错误

 9     return 0;

 10 }

 11void foo(int a,int b/*=200*/,int c/*=100*/){// 函数定义

 12    cout<<"a="<<a<<endl;

 13    cout<<"b="<<b<<endl;

 14    cout<<"c="<<c<<endl;

 15 }

3、 函数的哑元参数

1) 定义:只有类型没有变量名的形参称为哑元。  (实参传过来不用)

void func( int){

       …….

}

int main(){

       func(10);

}

2)作用:为了兼容以前的代码

算法库:void math_func( int a,int b) {…..}

使用者:

int main(void){

        math_func(10,20);

        …..

        math_func(102,202);

}

==è升级算法库:void math_func(int a,int /*哑元*/) { …..}

int main(void){

        math_func(10,20);

        …..

        math_func(102,202);

}

2) 运算符重载,区分前后++/--

例:

  1 #include<iostream>

  2 using namespace std;

  3 void foo(int /*哑元*/){

  4    cout<<"foo(int)"<<endl;

  5 }

  6 int main(void){

  7    foo(100);

  8    return 0;

  9 }

4、 内联函数inline

笔试题:inline关键字的作用

使用inline 修饰的函数,表示这个函数是一个内联函数,编译器将尝试做内联优化,避免函数调用的开销

(类似宏定义,只是宏定义在预处理执行,内联在编译执行。用空间换时间)

1) 多次调用小而简单的函数适合内联、

2) 调用次数极少或者大而复杂的函数不适合内联

3) 递归函数不适合内联

4) 内联只是一种建议,而不是一个强制的要求,能否做内联优化主要取决于编译器,有些函数不加inline关键字也会做内联优化,有些函数加了inline关键字也不用。

//笔试题

for(inti=0;i<10000000;i++){

       for(int j=0;j<1000;j++){

              //……

}

}

for(inti=0;i<1000;i++){

       for(int j=0;j<10000000;j++){

              //……

}

}

下面语句好,减少跳入跳出,连贯性好,提高程序执行效率

 

二、                           C++内存分配

C语言中:malloc( )/free( )

C++中,new /delete 运算符

new运算符用于动态内存分配,delete运算符用于动态内存释放

C:

例:在堆区动态分配内存

int *p=(int*)malloc(sizeof(int));

*p=100;

free(p);   如果不free释放,则内存泄露,浪费内存

p=NULL;

C++:

// int *p=newint;

// *p=100;

等价于 int *p=new int(100);

delete p;

p=NULL;

 

int *parr=newint [10]

parr[0]=10;

parr[1]=20;

*(parr+2)=30;

……

delete[ ] parr ;

parr = NULL;

例:new指针

  1 #include<iostream>

  2 using namespace std;

  3 int main(void){

  4    int *p1=new int;//分配4个字节的内存

  5    *p1=100;

  6    cout<<*p1<<endl;//100

  7    delete p1;// 如果不释放就再使用就会内存泄露

  8    p1=NULL;

  9

 10    p1=new int (200);//分配内存同时初始化

 11    cout<<*p1<<endl;//200

 12    /* 

 13    *p1++;   先++再取*;

 14    cout<<*p1<<endl; 段错误

 15    */

 16    (*p1)++;

 17    cout<<*p1<<endl; //201

 18    delete p1;

 19    p1=NULL;

 20    return 0;

 21 }

  new数组空间

1#include<iostream>

  2 using namespace std;

  3 int main(void){

  4    //分配一段连续的内存,sizeof(int)*10

  5    int *pa=new int[10];

  6    //new数组初始化,C++11标准中支持

  7    //int *pa=new int[10]{1,2,3,4,5,6,7,8,9,10};

  8    for(int i=0;i<10;i++){

  9        pa[i]=i+1;

 10        cout<<pa[i]<<' ';

 11     }

 12    cout<<endl;

 13    delete[] pa;//new[] 成对使用

 14    return 0;

 15 }

三、                           C++的引用(reference

1、定义引用就是给某个变量起别名,对引用的操作和对该变量的操作完全相同

int a=10;在栈区分配4个字节,

       int& b=a; // b就是a的别名

b++;

cout<<a<<endl; // 11

2、常引用

1)定义引用时加const修饰,即为常引用,不能通过常引用修改引用的目标

例:

int a=100;

const int&b=a;//b是a常引用

a=200;//OK

b=200;//ERROR

int &r=100;   r=200;错误   相当于100=200.

const int&r=100 ; 可以, 常量不做修改

2)普通的引用只能引用左值,常引用也叫做万能引用,既能够引用左值,也能够引用右值。

  1 #include<iostream>

  2 using namespace std;

  3 int main(void){

  4    // int& r=100;  error

  5    const int& r=100;//ok   常量不做修改

  6    return 0;

  7 }

3)关于左值和右值

左值:可以放在赋值运算符左侧,可以被修改,可以被取地址

int i;

i=100;

++i;

int *p=&i;

注:普通的变量都是左值,前缀表达式和赋值表达式也是左值

(++i)=200; //OK

Cout<<i<<endl;// 200

(i=300)=400;//OK

cout<<i<<endl;// 400

右值:只能放在赋值运算符右侧,不可被修改,不可被取地址

常见的右值:

注:1.字面值常量:10=i ;++10; int *p =&10;  都是错误的

2.大部分的表达式值都是右值:

int a=10;  int b=20;  (a+b)=30; //error  相当于30=30

3.函数的返回值

int foo(void){

       int a=100;

       return a;//编译会定义一个临时变量”temp”保存a

}

int main(void){

       int res=foo();//函数调用结果就是”temp”

       foo()=200;//error

       ++foo();//error

       int *p=&foo();//error

}

例:

  1 #include<iostream>

  2 using namespace std;

  3 int foo(void){

  4    int a=100;

  5    cout<<"&a="<<&a<<endl;

  6    return a;

  7 }

  8 int main(void){

  9    //int& r=foo();error

 10    //通过常引用引用函数返回的临时变量

 11    const int& r=foo();

 12    cout<<"&r"<<&r<<endl;//临时变量的地址

 13    int a=10;

 14    int b=20;

 15    //int& rc=a+b;error

 16    const int& rc=a+b;

 17    cout<<rc<<endl;//30

 18    char ch='A';

 19     //int& rch=ch;error

 20    const int& rch=ch;

 21    cout<<rch<<endl;

 22    return 0;

 23 }

tarena@tarena-virtual-machine:~$./a.out

&a=0xbfaa445c

&r0xbfaa448c

30

65

3、引用型函数的参数

1)将引用用于函数的参数,可以修改实参变量的值,可以减小函数调用的开销,避免实参到形参值的赋值。(形参直接引用实参,避免了开辟新的内存空间)

  1#include<iostream>

  2 using namespace std;

  3 void swap1(int x,int y){

  4    x=x^y;

  5    y=x^y;

  6    x=x^y;

  7 }

  8 void swap2(int *x,int *y){

  9    *x=*x^*y;

 10    *y=*x^*y;

 11    *x=*x^*y;

 12 }

 13 void swap3(int& x,int& y){//避免实虚结合,提高效率

 14    x=x^y;

 15    y=x^y;

 16    x=x^y;

 17 }

 18 void swap4(const char*& x,constchar*& y){//别名写在变量的前面,类型的后面

 19    const char *z=x;

 20    x=y;

 21    y=z;

 22 }

 23 int main(void){

24     int a=3;

 25    int b=5;

 26    swap1(a,b);

 27     cout<<"a="<<a<<endl;//3

 28    cout<<"b="<<b<<endl;//5

 29    swap2(&a,&b);

 30    cout<<"a="<<a<<endl;//5

 31    cout<<"b="<<b<<endl;//3

 32    swap3(a,b);

 33    cout<<"a="<<a<<endl;//3

 34    cout<<"b="<<b<<endl;//5

 35    const char* s1="hello";

 36    const char* s2="world";

 37    //实现swap4

 38    swap4(s1,s2);

 39    cout<<s1<<endl;//world

 40    cout<<s2<<endl;//hello

 41    return 0;

 42 }

2)引用型参数有可能意外修改实参变量的值,如果不希望修改实参本身,可以将形参定义为常引用,提高传参效率的同时,可以接受常量型的实参。

  1 #include<iostream>

  2 using namespace std;

  3 struct student{

  4    char name[100];

  5    int age;

  6 };

  7 //使用常引用:

  8 //1:提高传参效率

  9 //2:避免意外修改实参变量本身

 10 //3:可接受常量型的实参

 11 void print(const student& s){

 12    cout<<s.name<<','<<s.age<<endl;

 13 }

 14 int main(void){

 15    const student s={"tang",30};

 16    print(s);

 17    return 0;

 18 }

 

练习:总结引用的使用、引用和指针的区别

0 0