日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C/C++学习之路: C++对C的扩展

發(fā)布時(shí)間:2024/4/11 c/c++ 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++学习之路: C++对C的扩展 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

C/C++學(xué)習(xí)之路: C++對(duì)C的擴(kuò)展


1 ::作用域運(yùn)算符

  • 通常情況下,如果有兩個(gè)同名變量,一個(gè)是全局變量,另一個(gè)是局部變量,那么局部變量在其作用域內(nèi)具有較高的優(yōu)先權(quán),它將屏蔽全局變量。
  • int a = 10; //全局變量 void test(){int a = 20; //局部變量//全局a被隱藏cout << "a:" << a << endl; //a:20 }
  • 作用域運(yùn)算符可以用來(lái)解決局部變量與全局變量的重名問(wèn)題
  • int a = 10; //全局變量 //1. 局部變量和全局變量同名 void test(){int a = 20;//打印局部變量acout << "局部變量a:" << a << endl; // 局部變量a:20//打印全局變量acout << "全局變量a:" << ::a << endl; // 局部變量a:10 }
  • 可以看出,作用域運(yùn)算符可以用來(lái)解決局部變量與全局變量的重名問(wèn)題,即在局部變量的作用域內(nèi),可用::對(duì)被屏蔽的同名的全局變量進(jìn)行訪問(wèn)。

  • 2. 名字控制

  • 創(chuàng)建名字是程序設(shè)計(jì)過(guò)程中一項(xiàng)最基本的活動(dòng),當(dāng)一個(gè)項(xiàng)目很大時(shí),它會(huì)不可避免地包含大量名字。c++允許我們對(duì)名字的產(chǎn)生和名字的可見(jiàn)性進(jìn)行控制。
  • c語(yǔ)言可以通過(guò)static關(guān)鍵字來(lái)使得名字只得在本編譯單元內(nèi)可見(jiàn),在c++中將通過(guò)一種通過(guò)命名空間來(lái)控制對(duì)名字的訪問(wèn)。
  • 1. C++命名空間(namespace)

  • 在c++中,名稱(chēng)(name)可以是符號(hào)常量、變量、函數(shù)、結(jié)構(gòu)、枚舉、類(lèi)和對(duì)象等等。工程越大,名稱(chēng)互相沖突性的可能性越大。另外使用多個(gè)廠商的類(lèi)庫(kù)時(shí),也可能導(dǎo)致名稱(chēng)沖突。為了避免,在大規(guī)模程序的設(shè)計(jì)中,以及在程序員使用各種各樣的C++庫(kù)時(shí),這些標(biāo)識(shí)符的命名發(fā)生沖突,標(biāo)準(zhǔn)C++引入關(guān)鍵字namespace(命名空間/名字空間/名稱(chēng)空間),可以更好地控制標(biāo)識(shí)符的作用域。
  • 2. 命名空間使用語(yǔ)法

  • 命名空間只能全局范圍內(nèi)定義
  • 命名空間可嵌套命名空間
  • 命名空間是開(kāi)放的,即可以隨時(shí)把新的成員加入已有的命名空間中
  • 聲明和實(shí)現(xiàn)可分離
  • 無(wú)名命名空間,意味著命名空間中的標(biāo)識(shí)符只能在本文件內(nèi)訪問(wèn),相當(dāng)于給這個(gè)標(biāo)識(shí)符加上了static,使得其可以作為內(nèi)部連接
  • 可以有命名空間別名
  • 當(dāng)引入一個(gè)全局的using編譯指令時(shí),就為該文件打開(kāi)了該命名空間,它不會(huì)影響任何其他的文件,所以可以在每一個(gè)實(shí)現(xiàn)文件中調(diào)整對(duì)命名空間的控制。
  • 比如,如果發(fā)現(xiàn)某一個(gè)實(shí)現(xiàn)文件中有太多的using指令而產(chǎn)生的命名沖突,就要對(duì)該文件做個(gè)簡(jiǎn)單的改變,通過(guò)明確的限定或者using聲明來(lái)消除名字沖突,這樣不需要修改其他的實(shí)現(xiàn)文件。
  • namespace A { // 創(chuàng)建一個(gè)命名空間int a = 10;namespace C { // 命名空間可嵌套命名空間int c = 20;}void func1(); // 聲明和實(shí)現(xiàn)可分離 }void A::func1() {cout << "MySpace::func1" << endl; }namespace A { // 命名空間是開(kāi)放的,即可以隨時(shí)把新的成員加入已有的命名空間中void func() {cout << "hello namespace!" << endl;} }namespace B {int a = 20; }namespace { // 無(wú)名命名空間,意味著命名空間中的標(biāo)識(shí)符只能在本文件內(nèi)訪問(wèn),相當(dāng)于給這個(gè)標(biāo)識(shí)符加上了static,使得其可以作為內(nèi)部連接int a = 10;void func() { cout << "hello namespace" << endl; } }void test() {namespace A{ //錯(cuò)誤:命名空間只能全局范圍內(nèi)定義int a = 10;}cout << "A::a : " << A::a << endl;cout << "B::a : " << B::a << endl;cout << "a : " << a << endl;func();namespace shortName = B; // 命名空間別名cout << "veryLongName::a : " << shortName::a << endl; }

    3. using聲明

  • using聲明可使得指定的標(biāo)識(shí)符可用。
  • 如果命名空間包含一組用相同名字重載的函數(shù),using聲明就聲明了這個(gè)重載函數(shù)的所有集合。
  • namespace A {int paramA = 20;int paramB = 30;void funcA() { cout << "hello funcA" << endl; }void funcB() { cout << "hello funcA" << endl; }void func() {} // using聲明碰到函數(shù)重載void func(int x) {}int func(int x, int y) {}}void test() {//1. 通過(guò)命名空間域運(yùn)算符cout << A::paramA << endl;A::funcA();//2. using聲明using A::paramA; // using聲明可使得指定的標(biāo)識(shí)符可用using A::funcA;cout << paramA << endl;//cout << paramB << endl; //不可直接訪問(wèn)funcA();//3. 同名沖突//int paramA = 20; //相同作用域注意同名沖突using A::func; // using聲明碰到函數(shù)重載,如果命名空間包含一組用相同名字重載的函數(shù),using聲明就聲明了這個(gè)重載函數(shù)的所有集合。func();func(10);func(10, 20); }

    4. using編譯指令

  • using編譯指令使整個(gè)命名空間標(biāo)識(shí)符可用.
  • 使用using聲明或using編譯指令會(huì)增加命名沖突的可能性。如果有名稱(chēng)空間,并在代碼中使用作用域解析運(yùn)算符,則不會(huì)出現(xiàn)二義性。
  • namespace A {int paramA = 20;int paramB = 30;void funcA() { cout << "hello funcA" << endl; }void funcB() { cout << "hello funcB" << endl; } }namespace B {int paramA = 20;int paramB = 30;void funcA() { cout << "hello funcA" << endl; }void funcB() { cout << "hello funcB" << endl; } }void test01() {using namespace A;cout << paramA << endl;cout << paramB << endl;funcA();funcB();//不會(huì)產(chǎn)生二義性int paramA = 30;cout << paramA << endl;using namespace B;//二義性產(chǎn)生,不知道調(diào)用A還是B的paramA//cout << paramA << endl; //使用using聲明或using編譯指令會(huì)增加命名沖突的可能性。如果有名稱(chēng)空間,并在代碼中使用作用域解析運(yùn)算符,則不會(huì)出現(xiàn)二義性。 }

    3. struct類(lèi)型加強(qiáng)

  • c中定義結(jié)構(gòu)體變量需要加上struct關(guān)鍵字,c++不需要。
  • c中的結(jié)構(gòu)體只能定義成員變量,不能定義成員函數(shù)。c++即可以定義成員變量,也可以定義成員函數(shù)。
  • //1. 結(jié)構(gòu)體中即可以定義成員變量,也可以定義成員函數(shù) struct Student {string mName;int mAge;void setName(string name) { mName = name; }void setAge(int age) { mAge = age; }void showStudent() {cout << "Name:" << mName << " Age:" << mAge << endl;} };//2. c++中定義結(jié)構(gòu)體變量不需要加struct關(guān)鍵字 void test01() {Student student;student.setName("John");student.setAge(20);student.showStudent(); }

    4. 更嚴(yán)格的類(lèi)型轉(zhuǎn)換

  • 在C++,不同類(lèi)型的變量一般是不能直接賦值的,需要相應(yīng)的強(qiáng)轉(zhuǎn)。
  • c語(yǔ)言代碼:
  • typedef enum COLOR{ GREEN, RED, YELLOW } color; int main(){color mycolor = GREEN;mycolor = 10;printf("mycolor:%d\n", mycolor);char* p = malloc(10);return EXIT_SUCCESS; }
  • 以上c代碼c編譯器編譯可通過(guò),c++編譯器無(wú)法編譯通過(guò)。
  • 5. 三目運(yùn)算符功能增強(qiáng)

  • c語(yǔ)言三目運(yùn)算表達(dá)式返回值為數(shù)據(jù)值,為右值,不能賦值。
  • c++語(yǔ)言三目運(yùn)算表達(dá)式返回值為變量本身(引用),為左值,可以賦值。
  • 左值和右值概念:
  • 在c++中可以放在賦值操作符左邊的是左值,可以放到賦值操作符右面的是右值。
  • 有些變量即可以當(dāng)左值,也可以當(dāng)右值。
  • 左值為L(zhǎng)value,L代表Location,表示內(nèi)存可以尋址,可以賦值。
  • 右值為Rvalue,R代表Read,就是可以知道它的值。
  • 比如:int temp = 10; temp在內(nèi)存中有地址,10沒(méi)有,但是可以Read到它的值。
  • int a = 10; int b = 20; printf("ret:%d\n", a > b ? a : b); cout << "b:" << b << endl; //返回的是左值,變量的引用 (a > b ? a : b) = 100;//返回的是左值,變量的引用 cout << "b:" << b << endl;

    6. C/C++中的const

    1. const概述

    const單詞字面意思為常數(shù),不變的。它是c/c++中的一個(gè)關(guān)鍵字,是一個(gè)限定符,它用來(lái)限定一個(gè)變量不允許改變,它將一個(gè)對(duì)象轉(zhuǎn)換成一個(gè)常量。

    const int a = 10; A = 100; //編譯錯(cuò)誤,const是一個(gè)常量,不可修改

    2. C/C++中const的區(qū)別

    1. C中的const

  • c中的const理解為”一個(gè)不能改變的普通變量”,也就是認(rèn)為const應(yīng)該是一個(gè)只讀變量,既然是變量那么就會(huì)給const分配內(nèi)存,并且在c中const是一個(gè)全局只讀變量,c語(yǔ)言中const修飾的只讀變量是外部連接的。
  • 如果這么寫(xiě):
  • const int arrSize = 10; int arr[arrSize];
  • 看似是一件合理的編碼,但是這將得出一個(gè)錯(cuò)誤。 因?yàn)閍rrSize占用某塊內(nèi)存,所以C編譯器不知道它在編譯時(shí)的值是多少.
  • 2. C++中的const

  • 在c++中,一個(gè)const不必創(chuàng)建內(nèi)存空間,而在c中,一個(gè)const總是需要一塊內(nèi)存空間。
  • 在c++中,是否為const常量分配內(nèi)存空間依賴(lài)于如何使用。一般說(shuō)來(lái),如果一個(gè)const僅僅用來(lái)把一個(gè)名字用一個(gè)值代替(就像使用#define一樣),那么該存儲(chǔ)局空間就不必創(chuàng)建。
  • 如果存儲(chǔ)空間沒(méi)有分配內(nèi)存的話,在進(jìn)行完數(shù)據(jù)類(lèi)型檢查后,為了代碼更加有效,值也許會(huì)折疊到代碼中。
  • 不過(guò),取一個(gè)const地址, 或者把它定義為extern,則會(huì)為該const創(chuàng)建內(nèi)存空間。
  • 在c++中,出現(xiàn)在所有函數(shù)之外的const作用于整個(gè)文件(也就是說(shuō)它在該文件外不可見(jiàn)),默認(rèn)為內(nèi)部連接,c++中其他的標(biāo)識(shí)符一般默認(rèn)為外部連接。
  • 3. C/C++中const異同總結(jié)

  • c語(yǔ)言全局const會(huì)被存儲(chǔ)到只讀數(shù)據(jù)段。c++中全局const當(dāng)聲明extern或者對(duì)變量取地址時(shí),編譯器會(huì)分配存儲(chǔ)地址,變量存儲(chǔ)在只讀數(shù)據(jù)段。兩個(gè)都受到了只讀數(shù)據(jù)段的保護(hù),不可修改。
  • const int constA = 10;int main() {int *p = (int *) &constA;*p = 200;cout << *p << endl; }
  • 以上代碼在c/c++中編譯通過(guò),在運(yùn)行期,修改constA的值時(shí),發(fā)生寫(xiě)入錯(cuò)誤。原因是修改只讀數(shù)據(jù)段的數(shù)據(jù)。

  • c語(yǔ)言中局部const存儲(chǔ)在堆棧區(qū),只是不能通過(guò)變量直接修改const只讀變量的值,但是可以跳過(guò)編譯器的檢查,通過(guò)指針間接修改const值。

  • c++中對(duì)于局部的const變量要區(qū)別對(duì)待:

  • 對(duì)于基礎(chǔ)數(shù)據(jù)類(lèi)型,也就是const int a = 10這種,編譯器會(huì)進(jìn)行優(yōu)化,將值替換到訪問(wèn)的位置。
  • 對(duì)于基礎(chǔ)數(shù)據(jù)類(lèi)型,如果用一個(gè)變量初始化const變量,如果const int a = b,那么也是會(huì)給a分配內(nèi)存。
  • 對(duì)于自定數(shù)據(jù)類(lèi)型,比如類(lèi)對(duì)象,那么也會(huì)分配內(nèi)存。
  • const int constA = 10; int *q = (int *) &constA; *q = 300; printf("constA:%d\n", constA); printf("*p:%d\n", *q);int b = 10; const int constB = b; int *p = (int *) &constB; *p = 300; cout << "constB:" << constB << endl; cout << "*p:" << *p << endl;// 為person分配了內(nèi)存,所以我們可以通過(guò)指針的間接賦值修改person對(duì)象。 const Person person; //未初始化age //person.age = 50; //不可修改 Person *pPerson = (Person *) &person; //指針間接修改 pPerson->age = 100; cout << "pPerson->age:" << pPerson->age << endl; pPerson->age = 200; cout << "pPerson->age:" << pPerson->age << endl;
  • c中const默認(rèn)為外部連接,c++中const默認(rèn)為內(nèi)部連接.當(dāng)c語(yǔ)言?xún)蓚€(gè)文件中都有const int a的時(shí)候,編譯器會(huì)報(bào)重定義的錯(cuò)誤。而在c++中,則不會(huì),因?yàn)閏++中的const默認(rèn)是內(nèi)部連接的。如果想讓c++中的const具有外部連接,必須顯示聲明為: extern const int a = 10;
  • 擴(kuò)展:能否用變量定義數(shù)組
  • 在支持c99標(biāo)準(zhǔn)的編譯器中,可以使用變量定義數(shù)組。vs2013編譯器不支持c99,Linux GCC支持c99。
  • 3. 盡量以const替換#define

  • 在舊版本C中,如果想建立一個(gè)常量,必須使用預(yù)處理器
  • #define MAX 1024;// const int max = 1024

  • 我們定義的宏MAX從未被編譯器看到過(guò),因?yàn)樵陬A(yù)處理階段,所有的MAX已經(jīng)被替換為了1024,于是MAX并沒(méi)有將其加入到符號(hào)表中。但我們使用這個(gè)常量獲得一個(gè)編譯錯(cuò)誤信息時(shí),可能會(huì)帶來(lái)一些困惑,因?yàn)檫@個(gè)信息可能會(huì)提到1024,但是并沒(méi)有提到MAX。如果MAX被定義在一個(gè)不是你寫(xiě)的頭文件中,你可能并不知道1024代表什么,也許解決這個(gè)問(wèn)題要花費(fèi)很長(zhǎng)時(shí)間。
  • 解決辦法就是用一個(gè)常量替換上面的宏。
  • const int max= 1024;

  • const和#define區(qū)別總結(jié):

  • const有類(lèi)型,可進(jìn)行編譯器類(lèi)型安全檢查。#define無(wú)類(lèi)型,不可進(jìn)行類(lèi)型檢查.
  • const有作用域,而#define不重視作用域,默認(rèn)定義處到文件結(jié)尾.如果定義在指定作用域下有效的常量,那么#define就不能用。
  • 宏常量沒(méi)有類(lèi)型,所以調(diào)用了int類(lèi)型重載的函數(shù)。const有類(lèi)型,所以調(diào)用short類(lèi)型重載的函數(shù)

  • #define PARAM 128 const short param = 128;void func(short a) {cout << "short" << endl; }void func(int a) {cout << "int" << endl; }
  • 宏常量不重視作用域
  • void func1() {const int a = 10; #define A 20//#undef A //卸載宏常量A }void func2() {//cout << "a:" << a << endl; //不可訪問(wèn),超出了const int a作用域cout << "A:" << A << endl; //#define作用域從定義到文件結(jié)束或者到#undef,可訪問(wèn) }
  • 宏常量可以有命名空間
  • namespace MySpace{#define num 1024 }void test(){//cout << MySpace::NUM << endl; //錯(cuò)誤//int num = 100; //命名沖突cout << num << endl; }

    7. 引用(reference)

    1. 引用基本用法

  • 引用是c++對(duì)c的重要擴(kuò)充。在c/c++中指針的作用基本都是一樣的,但是c++增加了另外一種給函數(shù)傳遞地址的途徑,這就是按引用傳遞(pass-by-reference)

  • 變量名實(shí)質(zhì)上是一段連續(xù)內(nèi)存空間的別名,是一個(gè)標(biāo)號(hào)(門(mén)牌號(hào))
  • 程序中通過(guò)變量來(lái)申請(qǐng)并命名內(nèi)存空間
  • 通過(guò)變量的名字可以使用存儲(chǔ)空間
  • c++中新增了引用的概念,引用可以作為一個(gè)已定義變量的別名。

  • 基本語(yǔ)法:

  • Type& ref = val;
  • 注意事項(xiàng):
  • &在此不是求地址運(yùn)算,而是起標(biāo)識(shí)作用。
  • 類(lèi)型標(biāo)識(shí)符是指目標(biāo)變量的類(lèi)型
  • 必須在聲明引用變量時(shí)進(jìn)行初始化。
  • 引用初始化之后不能改變。
  • 不能有NULL引用。必須確保引用是和一塊合法的存儲(chǔ)單元關(guān)聯(lián)。
  • 建立對(duì)數(shù)組的引用。
  • void test01(){int a = 10;//給變量a取一個(gè)別名bint& b = a;cout << "a:" << a << endl;cout << "b:" << b << endl;cout << "------------" << endl;//操作b就相當(dāng)于操作a本身b = 100;cout << "a:" << a << endl;cout << "b:" << b << endl;cout << "------------" << endl;//一個(gè)變量可以有n個(gè)別名int& c = a;c = 200;cout << "a:" << a << endl;cout << "b:" << b << endl;cout << "c:" << c << endl;cout << "------------" << endl;//a,b,c的地址都是相同的cout << "a:" << &a << endl;cout << "b:" << &b << endl;cout << "c:" << &c << endl; }void test02() {//1) 引用必須初始化//int& ref; //報(bào)錯(cuò):必須初始化引用//2) 引用一旦初始化,不能改變引用int a = 10;int b = 20;int &ref = a;ref = b; //不能改變引用 }
  • 建立數(shù)組引用:
  • void test02() {//1. 建立數(shù)組引用方法一typedef int ArrRef[10];int arr[10];ArrRef &aRef = arr;for (int i = 0; i < 10; i++) {aRef[i] = i + 1;}for (int i = 0; i < 10; i++) {cout << arr[i] << " ";}cout << endl;//2. 建立數(shù)組引用方法二int(&f)[10] = arr;for (int i = 0; i < 10; i++) {f[i] = i + 10;}for (int i = 0; i < 10; i++) {cout << arr[i] << " ";}cout << endl; }

    2. 引用的本質(zhì)

  • 引用的本質(zhì)在c++內(nèi)部實(shí)現(xiàn)是一個(gè)常指針.
    Type& ref = val; // Type* const ref = &val;

  • c++編譯器在編譯過(guò)程中使用常指針作為引用的內(nèi)部實(shí)現(xiàn),因此引用所占用的空間大小與指針相同,只是這個(gè)過(guò)程是編譯器內(nèi)部實(shí)現(xiàn),用戶(hù)不可見(jiàn)。

  • //發(fā)現(xiàn)是引用,轉(zhuǎn)換為 int* const ref = &a; void testFunc(int& ref){ref = 100; // ref是引用,轉(zhuǎn)換為*ref = 100 } int main(){int a = 10;int& aRef = a; //自動(dòng)轉(zhuǎn)換為int* const aRef = &a;這也能說(shuō)明引用為什么必須初始化aRef = 20; //內(nèi)部發(fā)現(xiàn)aRef是引用,自動(dòng)幫我們轉(zhuǎn)換為: *aRef = 20;cout << "a:" << a << endl;cout << "aRef:" << aRef << endl;testFunc(a);return EXIT_SUCCESS; }

    3. 指針引用

  • 在c語(yǔ)言中如果想改變一個(gè)指針的指向而不是它所指向的內(nèi)容,函數(shù)聲明可能這樣:
  • void fun(int**);
  • 給指針變量取一個(gè)別名。
  • Type* pointer = NULL; Type*& = pointer; struct Teacher{int mAge; }; //指針間接修改teacher的年齡 void AllocateAndInitByPointer(Teacher** teacher){*teacher = (Teacher*)malloc(sizeof(Teacher));(*teacher)->mAge = 200; } //引用修改teacher年齡 void AllocateAndInitByReference(Teacher*& teacher){teacher->mAge = 300; } void test(){//創(chuàng)建TeacherTeacher* teacher = NULL;//指針間接賦值AllocateAndInitByPointer(&teacher);cout << "AllocateAndInitByPointer:" << teacher->mAge << endl;//引用賦值,將teacher本身傳到ChangeAgeByReference函數(shù)中AllocateAndInitByReference(teacher);cout << "AllocateAndInitByReference:" << teacher->mAge << endl;free(teacher); }
  • 對(duì)于c++中的定義那個(gè),語(yǔ)法清晰多了。函數(shù)參數(shù)變成指針的引用,用不著取得指針的地址。
  • 4. 常量引用

  • 常量引用的定義格式:
  • const Type& ref = val;
  • 常量引用注意:
  • 字面量不能賦給引用,但是可以賦給const引用
  • const修飾的引用,不能修改。
  • void test01(){int a = 100;const int& aRef = a; //此時(shí)aRef就是a//aRef = 200; 不能通過(guò)aRef的值a = 100; //OKcout << "a:" << a << endl;cout << "aRef:" << aRef << endl; } void test02(){//不能把一個(gè)字面量賦給引用//int& ref = 100;//但是可以把一個(gè)字面量賦給常引用const int& ref = 100; //int temp = 200; const int& ret = temp; }

    5. 引用使用場(chǎng)景

  • 常量引用主要用在函數(shù)的形參,尤其是類(lèi)的拷貝/復(fù)制構(gòu)造函數(shù)。
  • 將函數(shù)的形參定義為常量引用的好處:
  • 引用不產(chǎn)生新的變量,減少形參與實(shí)參傳遞時(shí)的開(kāi)銷(xiāo)。
  • 由于引用可能導(dǎo)致實(shí)參隨形參改變而改變,將其定義為常量引用可以消除這種副作用。
  • 如果希望實(shí)參隨著形參的改變而改變,那么使用一般的引用,如果不希望實(shí)參隨著形參改變,那么使用常引用。
  • //const int& param防止函數(shù)中意外修改數(shù)據(jù)
    void ShowVal(const int& param){
    cout << “param:” << param << endl;
    }

    1. 引用使用中注意點(diǎn)

  • 最常見(jiàn)看見(jiàn)引用的地方是在函數(shù)參數(shù)和返回值中。當(dāng)引用被用作函數(shù)參數(shù)的時(shí),在函數(shù)內(nèi)對(duì)任何引用的修改,將對(duì)還函數(shù)外的參數(shù)產(chǎn)生改變。當(dāng)然,可以通過(guò)傳遞一個(gè)指針來(lái)做相同的事情,但引用具有更清晰的語(yǔ)法。
  • 如果從函數(shù)中返回一個(gè)引用,必須像從函數(shù)中返回一個(gè)指針一樣對(duì)待。當(dāng)函數(shù)返回值時(shí),引用關(guān)聯(lián)的內(nèi)存一定要存在。
  • //值傳遞 void ValueSwap(int m,int n){int temp = m;m = n;n = temp; } //地址傳遞 void PointerSwap(int* m,int* n){int temp = *m;*m = *n;*n = temp; } //引用傳遞 void ReferenceSwap(int& m,int& n){int temp = m;m = n;n = temp; } void test(){int a = 10;int b = 20;//值傳遞ValueSwap(a, b);cout << "a:" << a << " b:" << b << endl;//地址傳遞PointerSwap(&a, &b);cout << "a:" << a << " b:" << b << endl;//引用傳遞ReferenceSwap(a, b);cout << "a:" << a << " b:" << b << endl; }
  • 通過(guò)引用參數(shù)產(chǎn)生的效果同按地址傳遞是一樣的。引用的語(yǔ)法更清楚簡(jiǎn)單:
  • 函數(shù)調(diào)用時(shí)傳遞的實(shí)參不必加“&”符
  • 在被調(diào)函數(shù)中不必在參數(shù)前加“*”符
  • 引用作為其它變量的別名而存在,因此在一些場(chǎng)合可以代替指針。C++主張用引用傳遞取代地址傳遞的方式,因?yàn)橐谜Z(yǔ)法容易且不易出錯(cuò)。
  • //返回局部變量引用 int& TestFun01(){int a = 10; //局部變量return a; } //返回靜態(tài)變量引用 int& TestFunc02(){ static int a = 20;cout << "static int a : " << a << endl;return a; } int main(){//不能返回局部變量的引用int& ret01 = TestFun01();//如果函數(shù)做左值,那么必須返回引用TestFunc02();TestFunc02() = 100;TestFunc02();return EXIT_SUCCESS; }

    8. 內(nèi)聯(lián)函數(shù)(inline function)

    1. 內(nèi)聯(lián)函數(shù)的引出

  • 在c中經(jīng)常把一些短并且執(zhí)行頻繁的計(jì)算寫(xiě)成宏,而不是函數(shù),這樣做的理由是為了執(zhí)行效率,宏可以避免函數(shù)調(diào)用的開(kāi)銷(xiāo),這些都由預(yù)處理來(lái)完成。

  • 但是在c++出現(xiàn)之后,使用預(yù)處理宏會(huì)出現(xiàn)兩個(gè)問(wèn)題:

  • 第一個(gè)在c中也會(huì)出現(xiàn),宏看起來(lái)像一個(gè)函數(shù)調(diào)用,但是會(huì)有隱藏一些難以發(fā)現(xiàn)的錯(cuò)誤。
  • 第二個(gè)問(wèn)題是c++特有的,預(yù)處理器不允許訪問(wèn)類(lèi)的成員,也就是說(shuō)預(yù)處理器宏不能用作類(lèi)類(lèi)的成員函數(shù)。
  • 為了保持預(yù)處理宏的效率又增加安全性,而且還能像一般成員函數(shù)那樣可以在類(lèi)里訪問(wèn)自如,c++引入了內(nèi)聯(lián)函數(shù)(inline function).

  • 內(nèi)聯(lián)函數(shù)為了繼承宏函數(shù)的效率,沒(méi)有函數(shù)調(diào)用時(shí)開(kāi)銷(xiāo),然后又可以像普通函數(shù)那樣,可以進(jìn)行參數(shù),返回值類(lèi)型的安全檢查,又可以作為成員函數(shù)。

  • 2. 預(yù)處理宏的缺陷

  • 預(yù)處理器宏存在問(wèn)題的關(guān)鍵是我們可能認(rèn)為預(yù)處理器的行為和編譯器的行為是一樣的。當(dāng)然也是由于宏函數(shù)調(diào)用和函數(shù)調(diào)用在外表看起來(lái)是一樣的,因?yàn)橐踩菀妆换煜5瞧渲幸矔?huì)有一些微妙的問(wèn)題出現(xiàn):
  • #define ADD(x,y) x+y inline int Add(int x,int y){return x + y; } void test(){int ret1 = ADD(10, 20) * 10; //希望的結(jié)果是300int ret2 = Add(10, 20) * 10; //希望結(jié)果也是300cout << "ret1:" << ret1 << endl; //210cout << "ret2:" << ret2 << endl; //300 }#define COMPARE(x,y) ((x) < (y) ? (x) : (y)) int Compare(int x,int y){return x < y ? x : y; } void test02(){int a = 1;int b = 3;//cout << "COMPARE(++a, b):" << COMPARE(++a, b) << endl; // 3cout << "Compare(int x,int y):" << Compare(++a, b) << endl; //2 }
  • 預(yù)定義宏函數(shù)沒(méi)有作用域概念,無(wú)法作為一個(gè)類(lèi)的成員函數(shù),也就是說(shuō)預(yù)定義宏沒(méi)有辦法表示類(lèi)的范圍。
  • 3. 內(nèi)聯(lián)函數(shù)

    1. 內(nèi)聯(lián)函數(shù)基本概念

  • 在c++中,預(yù)定義宏的概念是用內(nèi)聯(lián)函數(shù)來(lái)實(shí)現(xiàn)的,而內(nèi)聯(lián)函數(shù)本身也是一個(gè)真正的函數(shù)。內(nèi)聯(lián)函數(shù)具有普通函數(shù)的所有行為。唯一不同之處在于內(nèi)聯(lián)函數(shù)會(huì)在適當(dāng)?shù)牡胤较耦A(yù)定義宏一樣展開(kāi),所以不需要函數(shù)調(diào)用的開(kāi)銷(xiāo)。因此應(yīng)該不使用宏,使用內(nèi)聯(lián)函數(shù)。
  • 在普通函數(shù)(非成員函數(shù))函數(shù)前面加上inline關(guān)鍵字使之成為內(nèi)聯(lián)函數(shù)。但是必須注意必須函數(shù)體和聲明結(jié)合在一起,否則編譯器將它作為普通函數(shù)來(lái)對(duì)待。
  • inline void func(int a);
  • 以上寫(xiě)法沒(méi)有任何效果,僅僅是聲明函數(shù),應(yīng)該如下方式來(lái)做:
  • inline int func(int a){return a++;}
  • 注意: 編譯器將會(huì)檢查函數(shù)參數(shù)列表使用是否正確,并返回值(進(jìn)行必要的轉(zhuǎn)換)。這些事預(yù)處理器無(wú)法完成的。
  • 內(nèi)聯(lián)函數(shù)的確占用空間,但是內(nèi)聯(lián)函數(shù)相對(duì)于普通函數(shù)的優(yōu)勢(shì)只是省去了函數(shù)調(diào)用時(shí)候的壓棧,跳轉(zhuǎn),返回的開(kāi)銷(xiāo)。我們可以理解為內(nèi)聯(lián)函數(shù)是以空間換時(shí)間。
  • 2. 類(lèi)內(nèi)部的內(nèi)聯(lián)函數(shù)

  • 為了定義內(nèi)聯(lián)函數(shù),通常必須在函數(shù)定義前面放一個(gè)inline關(guān)鍵字。但是在類(lèi)內(nèi)部定義內(nèi)聯(lián)函數(shù)時(shí)并不是必須的。任何在類(lèi)內(nèi)部定義的函數(shù)自動(dòng)成為內(nèi)聯(lián)函數(shù)。
  • class Person{ public:Person(){ cout << "構(gòu)造函數(shù)!" << endl; }void PrintPerson(){ cout << "輸出Person!" << endl; } }
  • 構(gòu)造函數(shù)Person,成員函數(shù)PrintPerson在類(lèi)的內(nèi)部定義,自動(dòng)成為內(nèi)聯(lián)函數(shù)。
  • 3. 內(nèi)聯(lián)函數(shù)和編譯器

  • 內(nèi)聯(lián)函數(shù)并不是何時(shí)何地都有效,為了理解內(nèi)聯(lián)函數(shù)何時(shí)有效,應(yīng)該要知道編譯器碰到內(nèi)聯(lián)函數(shù)會(huì)怎么處理?

  • 對(duì)于任何類(lèi)型的函數(shù),編譯器會(huì)將函數(shù)類(lèi)型(包括函數(shù)名字,參數(shù)類(lèi)型,返回值類(lèi)型)放入到符號(hào)表中。

  • 同樣,當(dāng)編譯器看到內(nèi)聯(lián)函數(shù),并且對(duì)內(nèi)聯(lián)函數(shù)體進(jìn)行分析沒(méi)有發(fā)現(xiàn)錯(cuò)誤時(shí),也會(huì)將內(nèi)聯(lián)函數(shù)放入符號(hào)表。

  • 當(dāng)調(diào)用一個(gè)內(nèi)聯(lián)函數(shù)的時(shí)候,編譯器首先確保傳入?yún)?shù)類(lèi)型是正確匹配的,或者如果類(lèi)型不正完全匹配,但是可以將其轉(zhuǎn)換為正確類(lèi)型,并且返回值在目標(biāo)表達(dá)式里匹配正確類(lèi)型,或者可以轉(zhuǎn)換為目標(biāo)類(lèi)型,內(nèi)聯(lián)函數(shù)就會(huì)直接替換函數(shù)調(diào)用,這就消除了函數(shù)調(diào)用的開(kāi)銷(xiāo)。

  • 假如內(nèi)聯(lián)函數(shù)是成員函數(shù),對(duì)象this指針也會(huì)被放入合適位置。

  • 類(lèi)型檢查和類(lèi)型轉(zhuǎn)換、包括在合適位置放入對(duì)象this指針這些都是預(yù)處理器不能完成的。

  • 但是c++內(nèi)聯(lián)編譯會(huì)有一些限制,以下情況編譯器可能考慮不會(huì)將函數(shù)進(jìn)行內(nèi)聯(lián)編譯:

  • 不能存在任何形式的循環(huán)語(yǔ)句
  • 不能存在過(guò)多的條件判斷語(yǔ)句
  • 函數(shù)體不能過(guò)于龐大
  • 不能對(duì)函數(shù)進(jìn)行取址操作
  • 內(nèi)聯(lián)僅僅只是給編譯器一個(gè)建議,編譯器不一定會(huì)接受這種建議,如果你沒(méi)有將函數(shù)聲明為內(nèi)聯(lián)函數(shù),那么編譯器也可能將此函數(shù)做內(nèi)聯(lián)編譯。一個(gè)好的編譯器將會(huì)內(nèi)聯(lián)小的、簡(jiǎn)單的函數(shù)。


  • 9. 函數(shù)的默認(rèn)參數(shù)

  • c++在聲明函數(shù)原型的時(shí)可為一個(gè)或者多個(gè)參數(shù)指定默認(rèn)(缺省)的參數(shù)值,當(dāng)函數(shù)調(diào)用的時(shí)候如果沒(méi)有指定這個(gè)值,編譯器會(huì)自動(dòng)用默認(rèn)值代替。
  • void TestFunc01(int a = 10, int b = 20){
    cout << "a + b = " << a + b << endl;
    }

    //1. 形參b設(shè)置默認(rèn)參數(shù)值,那么后面位置的形參c也需要設(shè)置默認(rèn)參數(shù)
    void TestFunc02(int a,int b = 10,int c = 10){}
    //2. 如果函數(shù)聲明和函數(shù)定義分開(kāi),函數(shù)聲明設(shè)置了默認(rèn)參數(shù),函數(shù)定義不能再設(shè)置默認(rèn)參數(shù)
    void TestFunc03(int a = 0,int b = 0);
    void TestFunc03(int a, int b){}

    int main(){
    //1.如果沒(méi)有傳參數(shù),那么使用默認(rèn)參數(shù)
    TestFunc01();
    //2. 如果傳一個(gè)參數(shù),那么第二個(gè)參數(shù)使用默認(rèn)參數(shù)
    TestFunc01(100);
    //3. 如果傳入兩個(gè)參數(shù),那么兩個(gè)參數(shù)都使用我們傳入的參數(shù)
    TestFunc01(100, 200);

    return EXIT_SUCCESS;

    }


    10. 函數(shù)的占位參數(shù)

  • c++在聲明函數(shù)時(shí),可以設(shè)置占位參數(shù)。占位參數(shù)只有參數(shù)類(lèi)型聲明,而沒(méi)有參數(shù)名聲明。一般情況下,在函數(shù)體內(nèi)部無(wú)法使用占位參數(shù)。
  • void TestFunc01(int a,int b,int){//函數(shù)內(nèi)部無(wú)法使用占位參數(shù)cout << "a + b = " << a + b << endl; } //占位參數(shù)也可以設(shè)置默認(rèn)值 void TestFunc02(int a, int b, int = 20){//函數(shù)內(nèi)部依舊無(wú)法使用占位參數(shù)cout << "a + b = " << a + b << endl; } int main(){//錯(cuò)誤調(diào)用,占位參數(shù)也是參數(shù),必須傳參數(shù)//TestFunc01(10,20); //正確調(diào)用TestFunc01(10,20,30);//正確調(diào)用TestFunc02(10,20);//正確調(diào)用TestFunc02(10, 20, 30);return EXIT_SUCCESS; }
  • 在后面操作符重載的后置++要用到這個(gè)

  • 11. 函數(shù)重載(overload)

    1 函數(shù)重載概述

  • 在c++中同一個(gè)函數(shù)名在不同場(chǎng)景下可以具有不同的含義。
  • 在傳統(tǒng)c語(yǔ)言中,函數(shù)名必須是唯一的,程序中不允許出現(xiàn)同名的函數(shù)。在c++中是允許出現(xiàn)同名的函數(shù),這種現(xiàn)象稱(chēng)為函數(shù)重載。
    函數(shù)重載的目的就是為了方便的使用函數(shù)名。
  • 2. 函數(shù)重載

    1. 函數(shù)重載基本語(yǔ)法

  • 實(shí)現(xiàn)函數(shù)重載的條件:
  • 同一個(gè)作用域
  • 參數(shù)個(gè)數(shù)不同
  • 參數(shù)類(lèi)型不同
  • 參數(shù)順序不同
  • //1. 函數(shù)重載條件 namespace A{void MyFunc(){ cout << "無(wú)參數(shù)!" << endl; }void MyFunc(int a){ cout << "a: " << a << endl; }void MyFunc(string b){ cout << "b: " << b << endl; }void MyFunc(int a, string b){ cout << "a: " << a << " b:" << b << endl;}void MyFunc(string b, int a){cout << "a: " << a << " b:" << b << endl;} } //2.返回值不作為函數(shù)重載依據(jù) namespace B{void MyFunc(string b, int a){}//int MyFunc(string b, int a){} //無(wú)法重載僅按返回值區(qū)分的函數(shù) }
  • 注意: 函數(shù)重載和默認(rèn)參數(shù)一起使用,需要額外注意二義性問(wèn)題的產(chǎn)生。
  • void MyFunc(string b){cout << "b: " << b << endl; } //函數(shù)重載碰上默認(rèn)參數(shù) void MyFunc(string b, int a = 10){cout << "a: " << a << " b:" << b << endl; } int main(){MyFunc("hello"); //這時(shí),兩個(gè)函數(shù)都能匹配調(diào)用,產(chǎn)生二義性return 0; }
  • 為什么函數(shù)返回值不作為重載條件呢?
  • 當(dāng)編譯器能從上下文中確定唯一的函數(shù)的時(shí),如int ret = func(),這個(gè)當(dāng)然是沒(méi)有問(wèn)題的。然而,我們?cè)诰帉?xiě)程序過(guò)程中可以忽略他的返回值。
  • 那么這個(gè)時(shí)候,假如一個(gè)函數(shù)為 void func(int x);另一個(gè)為int func(int x);
  • 當(dāng)我們直接調(diào)用func(10),這個(gè)時(shí)候編譯器就不確定調(diào)用那個(gè)函數(shù)。所以在c++中禁止使用返回值作為重載的條件。
  • 2. 函數(shù)重載實(shí)現(xiàn)原理

  • 編譯器為了實(shí)現(xiàn)函數(shù)重載,編譯器用不同的參數(shù)類(lèi)型來(lái)修飾不同的函數(shù)名,比如void func(); 編譯器可能會(huì)將函數(shù)名修飾成_func,當(dāng)編譯器碰到void func(int x),編譯器可能將函數(shù)名修飾為func_int,當(dāng)編譯器碰到void func(int x,char c),編譯器可能會(huì)將函數(shù)名修飾為_(kāi)func_int_char
  • 不同的編譯器可能會(huì)產(chǎn)生不同的內(nèi)部名。
  • void func(){} void func(int x){} void func(int x,char y){}
  • 以上三個(gè)函數(shù)在linux下生成的編譯之后的函數(shù)名為:
  • _Z4funcv //v 代表void,無(wú)參數(shù) _Z4funci //i 代表參數(shù)為int類(lèi)型 _Z4funcic //i 代表第一個(gè)參數(shù)為int類(lèi)型,第二個(gè)參數(shù)為char類(lèi)型

    3. extern “C”淺析

  • 以下在Linux下測(cè)試:

  • c函數(shù): void MyFunc(){} ,被編譯成函數(shù): MyFunc
  • c++函數(shù): void MyFunc(){} ,被編譯成函數(shù): _Z6Myfuncv
  • 由于c++中需要支持函數(shù)重載,所以c和c++中對(duì)同一個(gè)函數(shù)經(jīng)過(guò)編譯后生成的函數(shù)名是不相同的,這就導(dǎo)致了一個(gè)問(wèn)題.

  • 如果在c++中調(diào)用一個(gè)使用c語(yǔ)言編寫(xiě)模塊中的某個(gè)函數(shù),那么c++是根據(jù)c++的名稱(chēng)修飾方式來(lái)查找并鏈接這個(gè)函數(shù),那么就會(huì)發(fā)生鏈接錯(cuò)誤,以上例,c++中調(diào)用MyFunc函數(shù),在鏈接階段會(huì)去找Z6Myfuncv,結(jié)果是沒(méi)有找到的,因?yàn)檫@個(gè)MyFunc函數(shù)是c語(yǔ)言編寫(xiě)的,生成的符號(hào)是MyFunc。

  • 那么如果想在c++調(diào)用c的函數(shù)怎么辦?

  • extern "C"的主要作用就是為了實(shí)現(xiàn)c++代碼能夠調(diào)用其他c語(yǔ)言代碼。加上extern "C"后,這部分代碼編譯器按c語(yǔ)言的方式進(jìn)行編譯和鏈接,而不是按c++的方式。
  • 總結(jié)

    以上是生活随笔為你收集整理的C/C++学习之路: C++对C的扩展的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。