1. 程式人生 > >C/C++(C++重載,默認參數,引用)

C/C++(C++重載,默認參數,引用)

end 常量 能夠 local clu char 原則 添加 上下文

C++重載詳解

重載就是同名而非同義,具體意義根據上下文的語境而言。
重載規則:

1,函數名相同。
2,參數個數不同,參數的類型不同,參數順序不同,均可構成重載。
3,返回值類型不同則不可以構成重載。
函數重載(靜多態)

void print(int a)
{
    //...
}
void print(int a,char b)
{
    //...
}
void print(char a,int a)
{
    //...
}
//根據參數的數序,個數執行上下文。

匹配原則:

1,嚴格匹配,找到則調用。
2,通過隱式轉換尋求一個匹配,找到則調用。

void print(int a)
{
    printf("void print(int a)
\n"); } void print(char a) { printf("void print(char a)\n"); } void print(float a) { printf("void print(float a)\n"); } void print(double a) { printf("void print(double a)\n"); } int main() { printf(3.4);//3.4是double類型,如果上述沒有double類型,他會轉向int,float類型,但是兩者同時又的話,會報錯產生二義性,不要讓計算機去解決一些事情。 print(‘a‘
);//如果沒有char類型最終會轉向int類型。字符是以asscII碼存在,也就是int類型。 }

int 到 long 和 double,double 到 int 和 float 隱式類型轉換。遇到這種情型,則會引起二義性。
如果使用時遇到二義性的,在使用時經過強轉即可。

void print(int a)
{
    printf("void print(int a)\n");
}
void print(char a)
{
    printf("void print(char a)\n");
}
void print(float a)
{
    printf("void print(float a)
\n"); } /*void print(double a) { printf("void print(double a)\n"); }*/ int main() { print(3.14);// error: call of overloaded ‘print(double)‘ is ambiguous print((int)3.14); print((float)3.14); /* void print(int a) void print(float a) */ }

重載的原理

name mangling(傾軋)

void print(int a)//print_i
{
    printf("void print(int a)\n");
}
void print(char a)//print_ic
{
    printf("void print(char a)\n");
}
void print(float a)//...
{
    printf("void print(float a)\n");
}
void print(double a)//...
{
    printf("void print(double a)\n");
}
int main()
{
    print(3.14);//重載函數之間底層處理技術傾軋,把相同的函數名傾軋成不同的函數名。
    return 0;
}

聲明.h文件

#ifndef PRING_H
#define PRING_H
extern "c"//不傾軋
{
    void print(int a)
    void print(char a)
    void print(int a,char b)
    void print(char a,int b)
}

#endif //PRING_H
#include<iostream>
#include "print.h"
using namespace std;
int main()
{
    int a,char b;
    print(a);
    return 0;
}

函數的定義部分.cpp文件

void print(int a)//print_i
{
    printf("void print(int a)\n");
}
void print(char a)//print_ic
{
    printf("void print(char a)\n");
}
void print(float a)//...
{
    printf("void print(float a)\n");
}
void print(double a)//...
{
    printf("void print(double a)\n");
}

要求是每個傾軋時要同時,不同的話則報錯。c++要完全兼容C,標準C庫是沒有傾軋的。但是寫頭文件時要包含的c++中,c++便宜的時候就會有傾軋的文件,這時候就會鏈接不上。所以為了連接上在自己寫的頭文件上要求不要傾軋。extern "c"或者extern "c" {多行的情況}

操作符重載

C++認為一切操作符都是函數,函數是可以重載的。=>操作符是可以重載的。(並不是所有的運算符都可以重載)

struct Complex{
    float a;
    float b;
}
int main(){
    Complex aa = {1,2},bb = {2,3};
    Complex cc = aa + bb;//error

    return 0;
}

對操作符進行重載

struct Complex
{
    float a;
    float b;
}
Complex operator+(Complex a,Complex b)
{
    Complex c;
    c.a = a.a + b.a;
    c.b = a.b + b.b;
    return c;
}
int main()
{
    Complex aa = {1,2},bb = {2,3};
    Complex cc = aa + bb;//right
    cout<<"cc = "<<"("<<cc.a<<","<<cc.b<<")"<<endl;
    return 0;
}
//cc = (3,5),實現了結構體之間的加法,當然也可以用函數實現一個加法的過程

默認參數(default parameters)

#include<iostream>
#include<time.h>
using namespace std;
void weatherCast(string w = "pm=2.5")//默認參數
{
    time_t t = time(0);//1970,0:0:0的毫秒數
    char tmp[64];
    strftime(tmp,sizeof(tmp),"%Y/%m/%d %x %A",localtime(&t));
    cout<<tmp<<"today is weather "<<w<<endl;
}
int main()
{
    weatherCast();
    weatherCast("sunshine");
    return 0;
}
//2018/03/04 03/04/18 Sundaytoday is weather pm=2.5
//2018/03/04 03/04/18 Sundaytoday is weather sunshine

多參數之間的默認參數,從右至左默認,中間不能跳躍。
實參的個數+默認的參數個數 >= 形參的個數

int volume(int l = 3,int w = 4,int h = 5)
{
    return l*w*h;
}
int main()
{
    cout<<volume()<<endl;
    cout<<volume(2,2)<<endl;
    cout<<volume(2,2,2)<<endl;
    return 0;
}
/*
60
20
8
*/

默認參數與重載的沖突

void print(int a)
{
    printf("void print(int a)");
}
void print(int a,int b = 100)
{
    printf("void print(int a,int b = 100)");
}
int main()
{
    print(1,200);
//void print(int a,int b = 200
    print(1);//報錯
    return 0;
}

一個參數或者是兩個參數的形式,重載,默認參數都是可以實現的,但是不可以兩者同時存在。(在函數重載實現默認參數時要註意,二義性)

引用

變量名,本身是一段內存的引用,即別名(alias)。此處引入的引用,是為己有變量起一個別名。

int main()
{
    int a = 500;//變量名的實質是一段內存空間的別名
    int&ra = a;//ra是a的引用
    printf("sizeof(ra) = %d sizeof(a) = %d\n",sizeof(ra),sizeof(a));
    printf("&a = %p &ra = %p\n",&a,&ra);
    a = 1000;
    printf("a = %d ra = %d\n",a,ra);
    ra = 1000;
    printf("a = %d ra = %d\n",a,ra);

    int b = 200;
//    int&ra = b;//報錯
    ra = b;//這是賦值

    int&rb = ra;
    printf("a = %d ra = %d ra = %d\n",a,ra,rb);
    int&rbc = rb;
    printf("a = %d ra = %d ra = %d rbc = %d\n",a,ra,rb,rbc);
    return 0;
}
/*
sizeof(ra) = 4 sizeof(a) = 4
&a = 0061fea8 &ra = 0061fea8
a = 1000 ra = 1000
a = 1000 ra = 1000
*/

/*
a = 1000 ra = 1000 ra = 1000
a = 1000 ra = 1000 ra = 1000 rbc = 1000
*/

1 引用是一種聲明關系,聲明的時候必須要初始化,引用不開辟空間
2 此種申明關系,一經聲明,不可變更
3 可以對引用再次引用,多次引用的結果是多個引用指向同一個變量

規則

1,引用沒有定義,是一種關系型聲明。聲明它和原有某一變量(實體)的關系。故而類型與原類型保持一致,且不分配內存。與被引用的變量有相同的地址。
2,聲明的時候必須初始化,一經聲明,不可變更。
3,可對引用,再次引用。多次引用的結果,是某一變量具有多個別名。
4,&符號前有數據類型時,是引用。其它皆為取地址。
應用場景:

void swap(int *pa,int *pb)
{
    *pa ^= *pb;
    *pb ^= *pa;
    *pa ^= *pb;
}
void swap1(int &pa,int &pb)//利用引用傳參,從c++的角度看沒有開辟空間
{
    &pa ^= &pb;
    &pb ^= &pa;
    &pa ^= &pb;
}
int main()
{
    int a = 3;int b = 5;
    int c = 3;int d = 5;
    swap(a,b);
    cout<<"a = "<<a<<" b = "<<b<<endl;
    swap1(c,d);
    cout<<"c = "<<c<<" d = "<<d<<endl;
    return 0;
}
/*
a = 5 b = 3
c = 5 d = 3
*/

指針的引用

void swap(char **pa,char **pb)
{
    char *t = pa;
    *pa = *pb;
    *pb = t;
}
int main()
{
    char *pa = "chaina";
    char *pb = "america";
    cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
    swap(pa,pb);//上述swap()函數中使用二級指針才可以實現交換。
    cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
    return 0;
}
/*
pa = chainapb = america
pa = americapb = chaina
*/

指針的引用可以實現平級內的交換,不開辟空間

void swap(char* &pa,char* &pb)
{
    char *t = pa;
    pa = pb;
    pb = t;
}
int main()
{
    char *pa = "chaina";
    char *pb = "america";
    cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
    swap(pa,pb);//上述swap()函數中使用二級指針才可以實現交換。
    cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
    return 0;
}
/*
pa = chainapb = america
pa = americapb = chaina
*/

引用的本質是對指針的再次包裝,指針是有引用的,不應該有引用的地址

引用的提高

引用的本質是指針,C++對裸露的內存地址(指針)作了一次包裝。又取得的指針的優良特性。所以再對引用取地址,建立引用的指針沒有意義。

1 可以定義指針的引用,但不能定義引用的引用

int *p;
int * & rp = p;//指針的引用

int a;
int & ra = a;
int & & raa = ra;//錯誤,沒有引用的引用

2 可以定義指針的指針(二級指針),但不能定義引用的指針。

int **p;//二級指針
int a;
int &ra = a;
int & * p = &ra;//引用本身就是對指針的包裝,再把引用打開包。與設計理念相悖。
//int * & ==>合法 指針的引用
//int & * ==>不合法 引用的指針

3,可以定義指針數組,但不能定義引用數組,可以定義數組引用。

int x,y,x;
int *p[] = {&x,&y,&z};//指針數組

int & rp[] = {x,y,x};//報錯,引用數組,rp首元素,相當於int & *

int arr[] = {1,2,3,4,5};//int * 類型 對他引用就變成int * &,指針的引用
int * &rarr = arr;//報錯,缺大小。
int (&rarr)[5] = arr;//right
int [5] &rarr = arr;//報錯
int &rarr [5] = arr;//error,引用的數組

常引用

const 引用有較多使用。它可以防止對象的值被隨意修改。因而具有一些特性。
(1)const 對象的引用必須是 const 的,將普通引用綁定到 const 對象是不合法的。這個原因比較簡單。既然對象是 const 的,表示不能被修改,引用當然也不能修改,必須使用 const 引用。實際上,const int a=1; int &b=a;這種寫法是不合法的,編譯不過。
(2)const 引用可使用相關類型的對象(常量,非同類型的變量或表達式)初始化。這個是const 引用與普通引用最大的區別。const int &a=2;是合法的。double x=3.14; const int&b=a;也是合法的。
const不可以改,也不可通過指針或者引用去改變。
常引用原理:
const 引用的目的是,禁止通過修改引用值來改變被引用的對象。const 引用的初始化特性較為微妙

const int a = 100;//對a取地址是 const int *
int *p = &a;//c中可以,c++中不可
const int *p = &a;//C++

const int &ra = a;//必須添加const


int a;
double & ra = a;//報錯,類型必須一致
const double & ra = a;//right

int y;
const int & ry = y;
int a = 200;
int & ra = a;
const double & rb = a;//他在底層用一個臨時變量交換,double tmp = a;const double &rd = tmp;const修飾無法改變。
cout<<"a = "<<a<<endl;
cout<<"ra = "<<ra<<endl;
cout<<"rb = "<<rb<<endl;
a = 400;
cout<<"a = "<<a<<endl;
cout<<"ra = "<<ra<<endl;
cout<<"rb = "<<rb<<endl;

cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
cout<<"&rb = "<<&rb<<endl;

/*
a = 200
ra = 200
rb = 200

a = 400
ra = 400
rb = 200

&a = 0x61fe8c
&ra = 0x61fe8c
&rb = 0x61fe90
*/

盡可能使用 const
use const whatever possible 原因如下:

1,使用 const 可以避免無意修改數據的編程錯誤。
2,使用 const 可以處理 const 和非 const 實參。否則將只能接受非 const 數據。
3,使用 const 引用,可使函數能夠正確的生成並使用臨時變量(如果實參與引用參數不匹配,就會生成臨時變量)

引用和指針實質是一樣的

C/C++(C++重載,默認參數,引用)