生活随笔
收集整理的這篇文章主要介紹了
C/C++学习之路: C++对C的扩展
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
C/C++學習之路: C++對C的擴展
1 ::作用域運算符
通常情況下,如果有兩個同名變量,一個是全局變量,另一個是局部變量,那么局部變量在其作用域內(nèi)具有較高的優(yōu)先權,它將屏蔽全局變量。
int a
= 10 ;
void test ( ) { int a
= 20 ; cout
<< "a:" << a
<< endl
;
}
作用域運算符可以用來解決局部變量與全局變量的重名問題
int a
= 10 ;
void test ( ) { int a
= 20 ; cout
<< "局部變量a:" << a
<< endl
; cout
<< "全局變量a:" << :: a
<< endl
;
}
可以看出,作用域運算符可以用來解決局部變量與全局變量的重名問題,即在局部變量的作用域內(nèi),可用::對被屏蔽的同名的全局變量進行訪問。
2. 名字控制
創(chuàng)建名字是程序設計過程中一項最基本的活動,當一個項目很大時,它會不可避免地包含大量名字。c++允許我們對名字的產(chǎn)生和名字的可見性進行控制。 c語言可以通過static關鍵字來使得名字只得在本編譯單元內(nèi)可見,在c++中將通過一種通過命名空間來控制對名字的訪問。
1. C++命名空間(namespace)
在c++中,名稱(name)可以是符號常量、變量、函數(shù)、結構、枚舉、類和對象等等。工程越大,名稱互相沖突性的可能性越大。另外使用多個廠商的類庫時,也可能導致名稱沖突。為了避免,在大規(guī)模程序的設計中,以及在程序員使用各種各樣的C++庫時,這些標識符的命名發(fā)生沖突,標準C++引入關鍵字namespace(命名空間/名字空間/名稱空間),可以更好地控制標識符的作用域。
2. 命名空間使用語法
命名空間只能全局范圍內(nèi)定義 命名空間可嵌套命名空間 命名空間是開放的,即可以隨時把新的成員加入已有的命名空間中 聲明和實現(xiàn)可分離 無名命名空間,意味著命名空間中的標識符只能在本文件內(nèi)訪問,相當于給這個標識符加上了static,使得其可以作為內(nèi)部連接 可以有命名空間別名 當引入一個全局的using編譯指令時,就為該文件打開了該命名空間,它不會影響任何其他的文件,所以可以在每一個實現(xiàn)文件中調(diào)整對命名空間的控制。 比如,如果發(fā)現(xiàn)某一個實現(xiàn)文件中有太多的using指令而產(chǎn)生的命名沖突,就要對該文件做個簡單的改變,通過明確的限定或者using聲明來消除名字沖突,這樣不需要修改其他的實現(xiàn)文件。
namespace A
{ int a
= 10 ; namespace C
{ int c
= 20 ; } void func1 ( ) ;
} void A
:: func1 ( ) { cout
<< "MySpace::func1" << endl
;
} namespace A
{ void func ( ) { cout
<< "hello namespace!" << endl
; }
} namespace B
{ int a
= 20 ;
} namespace
{ int a
= 10 ; void func ( ) { cout
<< "hello namespace" << endl
; }
} void test ( ) { namespace A
{ 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聲明可使得指定的標識符可用。 如果命名空間包含一組用相同名字重載的函數(shù),using聲明就聲明了這個重載函數(shù)的所有集合。
namespace A
{ int paramA
= 20 ; int paramB
= 30 ; void funcA ( ) { cout
<< "hello funcA" << endl
; } void funcB ( ) { cout
<< "hello funcA" << endl
; } void func ( ) { } void func ( int x
) { } int func ( int x
, int y
) { } } void test ( ) { cout
<< A
:: paramA
<< endl
; A
:: funcA ( ) ; using A
:: paramA
; using A
:: funcA
; cout
<< paramA
<< endl
; funcA ( ) ; using A
:: func
; func ( ) ; func ( 10 ) ; func ( 10 , 20 ) ;
}
4. using編譯指令
using編譯指令使整個命名空間標識符可用. 使用using聲明或using編譯指令會增加命名沖突的可能性。如果有名稱空間,并在代碼中使用作用域解析運算符,則不會出現(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 ( ) ; int paramA
= 30 ; cout
<< paramA
<< endl
; using namespace B
;
}
3. struct類型加強
c中定義結構體變量需要加上struct關鍵字,c++不需要。 c中的結構體只能定義成員變量,不能定義成員函數(shù)。c++即可以定義成員變量,也可以定義成員函數(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
; }
} ;
void test01 ( ) { Student student
; student
. setName ( "John" ) ; student
. setAge ( 20 ) ; student
. showStudent ( ) ;
}
4. 更嚴格的類型轉換
在C++,不同類型的變量一般是不能直接賦值的,需要相應的強轉。 c語言代碼:
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編譯器編譯可通過,c++編譯器無法編譯通過。
5. 三目運算符功能增強
c語言三目運算表達式返回值為數(shù)據(jù)值,為右值,不能賦值。 c++語言三目運算表達式返回值為變量本身(引用),為左值,可以賦值。 左值和右值概念: 在c++中可以放在賦值操作符左邊的是左值,可以放到賦值操作符右面的是右值。 有些變量即可以當左值,也可以當右值。 左值為Lvalue,L代表Location,表示內(nèi)存可以尋址,可以賦值。 右值為Rvalue,R代表Read,就是可以知道它的值。 比如:int temp = 10; temp在內(nèi)存中有地址,10沒有,但是可以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++中的一個關鍵字,是一個限定符,它用來限定一個變量不允許改變,它將一個對象轉換成一個常量。
const int a
= 10 ;
A
= 100 ;
2. C/C++中const的區(qū)別
1. C中的const
c中的const理解為”一個不能改變的普通變量”,也就是認為const應該是一個只讀變量,既然是變量那么就會給const分配內(nèi)存,并且在c中const是一個全局只讀變量,c語言中const修飾的只讀變量是外部連接的。 如果這么寫:
const int arrSize
= 10 ;
int arr
[ arrSize
] ;
看似是一件合理的編碼,但是這將得出一個錯誤。 因為arrSize占用某塊內(nèi)存,所以C編譯器不知道它在編譯時的值是多少.
2. C++中的const
在c++中,一個const不必創(chuàng)建內(nèi)存空間,而在c中,一個const總是需要一塊內(nèi)存空間。 在c++中,是否為const常量分配內(nèi)存空間依賴于如何使用。一般說來,如果一個const僅僅用來把一個名字用一個值代替(就像使用#define一樣),那么該存儲局空間就不必創(chuàng)建。 如果存儲空間沒有分配內(nèi)存的話,在進行完數(shù)據(jù)類型檢查后,為了代碼更加有效,值也許會折疊到代碼中。 不過,取一個const地址, 或者把它定義為extern,則會為該const創(chuàng)建內(nèi)存空間。 在c++中,出現(xiàn)在所有函數(shù)之外的const作用于整個文件(也就是說它在該文件外不可見),默認為內(nèi)部連接,c++中其他的標識符一般默認為外部連接。
3. C/C++中const異同總結
c語言全局const會被存儲到只讀數(shù)據(jù)段。c++中全局const當聲明extern或者對變量取地址時,編譯器會分配存儲地址,變量存儲在只讀數(shù)據(jù)段。兩個都受到了只讀數(shù)據(jù)段的保護,不可修改。
const int constA
= 10 ; int main ( ) { int * p
= ( int * ) & constA
; * p
= 200 ; cout
<< * p
<< endl
;
}
以上代碼在c/c++中編譯通過,在運行期,修改constA的值時,發(fā)生寫入錯誤。原因是修改只讀數(shù)據(jù)段的數(shù)據(jù)。
c語言中局部const存儲在堆棧區(qū),只是不能通過變量直接修改const只讀變量的值,但是可以跳過編譯器的檢查,通過指針間接修改const值。
c++中對于局部的const變量要區(qū)別對待:
對于基礎數(shù)據(jù)類型,也就是const int a = 10這種,編譯器會進行優(yōu)化,將值替換到訪問的位置。 對于基礎數(shù)據(jù)類型,如果用一個變量初始化const變量,如果const int a = b,那么也是會給a分配內(nèi)存。 對于自定數(shù)據(jù)類型,比如類對象,那么也會分配內(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
;
const Person person
;
Person
* pPerson
= ( Person
* ) & person
;
pPerson
-> age
= 100 ;
cout
<< "pPerson->age:" << pPerson
-> age
<< endl
;
pPerson
-> age
= 200 ;
cout
<< "pPerson->age:" << pPerson
-> age
<< endl
;
c中const默認為外部連接,c++中const默認為內(nèi)部連接.當c語言兩個文件中都有const int a的時候,編譯器會報重定義的錯誤。而在c++中,則不會,因為c++中的const默認是內(nèi)部連接的。如果想讓c++中的const具有外部連接,必須顯示聲明為: extern const int a = 10; 擴展:能否用變量定義數(shù)組 在支持c99標準的編譯器中,可以使用變量定義數(shù)組。vs2013編譯器不支持c99,Linux GCC支持c99。
3. 盡量以const替換#define
在舊版本C中,如果想建立一個常量,必須使用預處理器
#define MAX 1024;// const int max = 1024
我們定義的宏MAX從未被編譯器看到過,因為在預處理階段,所有的MAX已經(jīng)被替換為了1024,于是MAX并沒有將其加入到符號表中。但我們使用這個常量獲得一個編譯錯誤信息時,可能會帶來一些困惑,因為這個信息可能會提到1024,但是并沒有提到MAX。如果MAX被定義在一個不是你寫的頭文件中,你可能并不知道1024代表什么,也許解決這個問題要花費很長時間。 解決辦法就是用一個常量替換上面的宏。
const int max= 1024;
const和#define區(qū)別總結:
const有類型,可進行編譯器類型安全檢查。#define無類型,不可進行類型檢查. const有作用域,而#define不重視作用域,默認定義處到文件結尾.如果定義在指定作用域下有效的常量,那么#define就不能用。 宏常量沒有類型,所以調(diào)用了int類型重載的函數(shù)。const有類型,所以調(diào)用short類型重載的函數(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
} void func2 ( ) { cout
<< "A:" << A
<< endl
;
}
宏常量可以有命名空間
namespace MySpace
{ # define num 1024
} void test ( ) { cout
<< num
<< endl
;
}
7. 引用(reference)
1. 引用基本用法
引用是c++對c的重要擴充。在c/c++中指針的作用基本都是一樣的,但是c++增加了另外一種給函數(shù)傳遞地址的途徑,這就是按引用傳遞(pass-by-reference)
變量名實質上是一段連續(xù)內(nèi)存空間的別名,是一個標號(門牌號) 程序中通過變量來申請并命名內(nèi)存空間 通過變量的名字可以使用存儲空間 c++中新增了引用的概念,引用可以作為一個已定義變量的別名。
基本語法:
Type
& ref
= val
;
注意事項: &在此不是求地址運算,而是起標識作用。 類型標識符是指目標變量的類型 必須在聲明引用變量時進行初始化。 引用初始化之后不能改變。 不能有NULL引用。必須確保引用是和一塊合法的存儲單元關聯(lián)。 建立對數(shù)組的引用。
void test01 ( ) { int a
= 10 ; int & b
= a
; cout
<< "a:" << a
<< endl
; cout
<< "b:" << b
<< endl
; cout
<< "------------" << endl
; b
= 100 ; cout
<< "a:" << a
<< endl
; cout
<< "b:" << b
<< endl
; cout
<< "------------" << endl
; int & c
= a
; c
= 200 ; cout
<< "a:" << a
<< endl
; cout
<< "b:" << b
<< endl
; cout
<< "c:" << c
<< endl
; cout
<< "------------" << endl
; cout
<< "a:" << & a
<< endl
; cout
<< "b:" << & b
<< endl
; cout
<< "c:" << & c
<< endl
;
} void test02 ( ) { int a
= 10 ; int b
= 20 ; int & ref
= a
; ref
= b
;
}
建立數(shù)組引用:
void test02 ( ) { 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
; 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. 引用的本質
引用的本質在c++內(nèi)部實現(xiàn)是一個常指針. Type& ref = val; // Type* const ref = &val;
c++編譯器在編譯過程中使用常指針作為引用的內(nèi)部實現(xiàn),因此引用所占用的空間大小與指針相同,只是這個過程是編譯器內(nèi)部實現(xiàn),用戶不可見。
void testFunc ( int & ref
) { ref
= 100 ;
}
int main ( ) { int a
= 10 ; int & aRef
= a
; aRef
= 20 ; cout
<< "a:" << a
<< endl
; cout
<< "aRef:" << aRef
<< endl
; testFunc ( a
) ; return EXIT_SUCCESS
;
}
3. 指針引用
在c語言中如果想改變一個指針的指向而不是它所指向的內(nèi)容,函數(shù)聲明可能這樣:
void fun ( int * * ) ;
給指針變量取一個別名。
Type
* pointer
= NULL ;
Type
* & = pointer
;
struct Teacher { int mAge
;
} ;
void AllocateAndInitByPointer ( Teacher
* * teacher
) { * teacher
= ( Teacher
* ) malloc ( sizeof ( Teacher
) ) ; ( * teacher
) -> mAge
= 200 ;
}
void AllocateAndInitByReference ( Teacher
* & teacher
) { teacher
-> mAge
= 300 ;
}
void test ( ) { Teacher
* teacher
= NULL ; AllocateAndInitByPointer ( & teacher
) ; cout
<< "AllocateAndInitByPointer:" << teacher
-> mAge
<< endl
; AllocateAndInitByReference ( teacher
) ; cout
<< "AllocateAndInitByReference:" << teacher
-> mAge
<< endl
; free ( teacher
) ;
}
對于c++中的定義那個,語法清晰多了。函數(shù)參數(shù)變成指針的引用,用不著取得指針的地址。
4. 常量引用
常量引用的定義格式:
const Type
& ref
= val
;
常量引用注意: 字面量不能賦給引用,但是可以賦給const引用 const修飾的引用,不能修改。
void test01 ( ) { int a
= 100 ; const int & aRef
= a
; a
= 100 ; cout
<< "a:" << a
<< endl
; cout
<< "aRef:" << aRef
<< endl
;
}
void test02 ( ) { const int & ref
= 100 ;
}
5. 引用使用場景
常量引用主要用在函數(shù)的形參,尤其是類的拷貝/復制構造函數(shù)。 將函數(shù)的形參定義為常量引用的好處: 引用不產(chǎn)生新的變量,減少形參與實參傳遞時的開銷。 由于引用可能導致實參隨形參改變而改變,將其定義為常量引用可以消除這種副作用。 如果希望實參隨著形參的改變而改變,那么使用一般的引用,如果不希望實參隨著形參改變,那么使用常引用。
//const int& param防止函數(shù)中意外修改數(shù)據(jù) void ShowVal(const int& param){ cout << “param:” << param << endl; }
1. 引用使用中注意點
最常見看見引用的地方是在函數(shù)參數(shù)和返回值中。當引用被用作函數(shù)參數(shù)的時,在函數(shù)內(nèi)對任何引用的修改,將對還函數(shù)外的參數(shù)產(chǎn)生改變。當然,可以通過傳遞一個指針來做相同的事情,但引用具有更清晰的語法。 如果從函數(shù)中返回一個引用,必須像從函數(shù)中返回一個指針一樣對待。當函數(shù)返回值時,引用關聯(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
;
}
通過引用參數(shù)產(chǎn)生的效果同按地址傳遞是一樣的。引用的語法更清楚簡單: 函數(shù)調(diào)用時傳遞的實參不必加“&”符 在被調(diào)函數(shù)中不必在參數(shù)前加“*”符 引用作為其它變量的別名而存在,因此在一些場合可以代替指針。C++主張用引用傳遞取代地址傳遞的方式,因為引用語法容易且不易出錯。
int & TestFun01 ( ) { int a
= 10 ; return a
;
}
int & TestFunc02 ( ) { static int a
= 20 ; cout
<< "static int a : " << a
<< endl
; return a
;
}
int main ( ) { int & ret01
= TestFun01 ( ) ; 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í)行頻繁的計算寫成宏,而不是函數(shù),這樣做的理由是為了執(zhí)行效率,宏可以避免函數(shù)調(diào)用的開銷,這些都由預處理來完成。
但是在c++出現(xiàn)之后,使用預處理宏會出現(xiàn)兩個問題:
第一個在c中也會出現(xiàn),宏看起來像一個函數(shù)調(diào)用,但是會有隱藏一些難以發(fā)現(xiàn)的錯誤。 第二個問題是c++特有的,預處理器不允許訪問類的成員,也就是說預處理器宏不能用作類類的成員函數(shù)。 為了保持預處理宏的效率又增加安全性,而且還能像一般成員函數(shù)那樣可以在類里訪問自如,c++引入了內(nèi)聯(lián)函數(shù)(inline function).
內(nèi)聯(lián)函數(shù)為了繼承宏函數(shù)的效率,沒有函數(shù)調(diào)用時開銷,然后又可以像普通函數(shù)那樣,可以進行參數(shù),返回值類型的安全檢查,又可以作為成員函數(shù)。
2. 預處理宏的缺陷
預處理器宏存在問題的關鍵是我們可能認為預處理器的行為和編譯器的行為是一樣的。當然也是由于宏函數(shù)調(diào)用和函數(shù)調(diào)用在外表看起來是一樣的,因為也容易被混淆。但是其中也會有一些微妙的問題出現(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 ; int ret2
= Add ( 10 , 20 ) * 10 ; cout
<< "ret1:" << ret1
<< endl
; cout
<< "ret2:" << ret2
<< endl
;
} # 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(int x,int y):" << Compare ( ++ a
, b
) << endl
;
}
預定義宏函數(shù)沒有作用域概念,無法作為一個類的成員函數(shù),也就是說預定義宏沒有辦法表示類的范圍。
3. 內(nèi)聯(lián)函數(shù)
1. 內(nèi)聯(lián)函數(shù)基本概念
在c++中,預定義宏的概念是用內(nèi)聯(lián)函數(shù)來實現(xiàn)的,而內(nèi)聯(lián)函數(shù)本身也是一個真正的函數(shù)。內(nèi)聯(lián)函數(shù)具有普通函數(shù)的所有行為。唯一不同之處在于內(nèi)聯(lián)函數(shù)會在適當?shù)牡胤较耦A定義宏一樣展開,所以不需要函數(shù)調(diào)用的開銷。因此應該不使用宏,使用內(nèi)聯(lián)函數(shù)。 在普通函數(shù)(非成員函數(shù))函數(shù)前面加上inline關鍵字使之成為內(nèi)聯(lián)函數(shù)。但是必須注意必須函數(shù)體和聲明結合在一起,否則編譯器將它作為普通函數(shù)來對待。
inline void func ( int a
) ;
以上寫法沒有任何效果,僅僅是聲明函數(shù),應該如下方式來做:
inline int func ( int a
) { return a
++ ; }
注意: 編譯器將會檢查函數(shù)參數(shù)列表使用是否正確,并返回值(進行必要的轉換)。這些事預處理器無法完成的。 內(nèi)聯(lián)函數(shù)的確占用空間,但是內(nèi)聯(lián)函數(shù)相對于普通函數(shù)的優(yōu)勢只是省去了函數(shù)調(diào)用時候的壓棧,跳轉,返回的開銷。我們可以理解為內(nèi)聯(lián)函數(shù)是以空間換時間。
2. 類內(nèi)部的內(nèi)聯(lián)函數(shù)
為了定義內(nèi)聯(lián)函數(shù),通常必須在函數(shù)定義前面放一個inline關鍵字。但是在類內(nèi)部定義內(nèi)聯(lián)函數(shù)時并不是必須的。任何在類內(nèi)部定義的函數(shù)自動成為內(nèi)聯(lián)函數(shù)。
class Person
{
public
: Person ( ) { cout
<< "構造函數(shù)!" << endl
; } void PrintPerson ( ) { cout
<< "輸出Person!" << endl
; }
}
構造函數(shù)Person,成員函數(shù)PrintPerson在類的內(nèi)部定義,自動成為內(nèi)聯(lián)函數(shù)。
3. 內(nèi)聯(lián)函數(shù)和編譯器
內(nèi)聯(lián)函數(shù)并不是何時何地都有效,為了理解內(nèi)聯(lián)函數(shù)何時有效,應該要知道編譯器碰到內(nèi)聯(lián)函數(shù)會怎么處理?
對于任何類型的函數(shù),編譯器會將函數(shù)類型(包括函數(shù)名字,參數(shù)類型,返回值類型)放入到符號表中。
同樣,當編譯器看到內(nèi)聯(lián)函數(shù),并且對內(nèi)聯(lián)函數(shù)體進行分析沒有發(fā)現(xiàn)錯誤時,也會將內(nèi)聯(lián)函數(shù)放入符號表。
當調(diào)用一個內(nèi)聯(lián)函數(shù)的時候,編譯器首先確保傳入?yún)?shù)類型是正確匹配的,或者如果類型不正完全匹配,但是可以將其轉換為正確類型,并且返回值在目標表達式里匹配正確類型,或者可以轉換為目標類型,內(nèi)聯(lián)函數(shù)就會直接替換函數(shù)調(diào)用,這就消除了函數(shù)調(diào)用的開銷。
假如內(nèi)聯(lián)函數(shù)是成員函數(shù),對象this指針也會被放入合適位置。
類型檢查和類型轉換、包括在合適位置放入對象this指針這些都是預處理器不能完成的。
但是c++內(nèi)聯(lián)編譯會有一些限制,以下情況編譯器可能考慮不會將函數(shù)進行內(nèi)聯(lián)編譯:
不能存在任何形式的循環(huán)語句 不能存在過多的條件判斷語句 函數(shù)體不能過于龐大 不能對函數(shù)進行取址操作 內(nèi)聯(lián)僅僅只是給編譯器一個建議,編譯器不一定會接受這種建議,如果你沒有將函數(shù)聲明為內(nèi)聯(lián)函數(shù),那么編譯器也可能將此函數(shù)做內(nèi)聯(lián)編譯。一個好的編譯器將會內(nèi)聯(lián)小的、簡單的函數(shù)。
9. 函數(shù)的默認參數(shù)
c++在聲明函數(shù)原型的時可為一個或者多個參數(shù)指定默認(缺省)的參數(shù)值,當函數(shù)調(diào)用的時候如果沒有指定這個值,編譯器會自動用默認值代替。
void TestFunc01(int a = 10, int b = 20){ cout << "a + b = " << a + b << endl; }
//1. 形參b設置默認參數(shù)值,那么后面位置的形參c也需要設置默認參數(shù) void TestFunc02(int a,int b = 10,int c = 10){} //2. 如果函數(shù)聲明和函數(shù)定義分開,函數(shù)聲明設置了默認參數(shù),函數(shù)定義不能再設置默認參數(shù) void TestFunc03(int a = 0,int b = 0); void TestFunc03(int a, int b){}
int main(){ //1.如果沒有傳參數(shù),那么使用默認參數(shù) TestFunc01(); //2. 如果傳一個參數(shù),那么第二個參數(shù)使用默認參數(shù) TestFunc01(100); //3. 如果傳入兩個參數(shù),那么兩個參數(shù)都使用我們傳入的參數(shù) TestFunc01(100, 200);
return EXIT_SUCCESS;
}
10. 函數(shù)的占位參數(shù)
c++在聲明函數(shù)時,可以設置占位參數(shù)。占位參數(shù)只有參數(shù)類型聲明,而沒有參數(shù)名聲明。一般情況下,在函數(shù)體內(nèi)部無法使用占位參數(shù)。
void TestFunc01 ( int a
, int b
, int ) { cout
<< "a + b = " << a
+ b
<< endl
;
}
void TestFunc02 ( int a
, int b
, int = 20 ) { cout
<< "a + b = " << a
+ b
<< endl
;
}
int main ( ) { TestFunc01 ( 10 , 20 , 30 ) ; TestFunc02 ( 10 , 20 ) ; TestFunc02 ( 10 , 20 , 30 ) ; return EXIT_SUCCESS
;
}
在后面操作符重載的后置++要用到這個
11. 函數(shù)重載(overload)
1 函數(shù)重載概述
在c++中同一個函數(shù)名在不同場景下可以具有不同的含義。 在傳統(tǒng)c語言中,函數(shù)名必須是唯一的,程序中不允許出現(xiàn)同名的函數(shù)。在c++中是允許出現(xiàn)同名的函數(shù),這種現(xiàn)象稱為函數(shù)重載。 函數(shù)重載的目的就是為了方便的使用函數(shù)名。
2. 函數(shù)重載
1. 函數(shù)重載基本語法
實現(xiàn)函數(shù)重載的條件: 同一個作用域 參數(shù)個數(shù)不同 參數(shù)類型不同 參數(shù)順序不同
namespace A
{ void MyFunc ( ) { cout
<< "無參數(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
; }
}
namespace B
{ void MyFunc ( string b
, int a
) { }
}
注意: 函數(shù)重載和默認參數(shù)一起使用,需要額外注意二義性問題的產(chǎn)生。
void MyFunc ( string b
) { cout
<< "b: " << b
<< endl
;
}
void MyFunc ( string b
, int a
= 10 ) { cout
<< "a: " << a
<< " b:" << b
<< endl
;
}
int main ( ) { MyFunc ( "hello" ) ; return 0 ;
}
為什么函數(shù)返回值不作為重載條件呢? 當編譯器能從上下文中確定唯一的函數(shù)的時,如int ret = func(),這個當然是沒有問題的。然而,我們在編寫程序過程中可以忽略他的返回值。 那么這個時候,假如一個函數(shù)為 void func(int x);另一個為int func(int x); 當我們直接調(diào)用func(10),這個時候編譯器就不確定調(diào)用那個函數(shù)。所以在c++中禁止使用返回值作為重載的條件。
2. 函數(shù)重載實現(xiàn)原理
編譯器為了實現(xiàn)函數(shù)重載,編譯器用不同的參數(shù)類型來修飾不同的函數(shù)名,比如void func(); 編譯器可能會將函數(shù)名修飾成_func,當編譯器碰到void func(int x),編譯器可能將函數(shù)名修飾為func_int,當編譯器碰到void func(int x,char c),編譯器可能會將函數(shù)名修飾為_func_int_char 不同的編譯器可能會產(chǎn)生不同的內(nèi)部名。
void func ( ) { }
void func ( int x
) { }
void func ( int x
, char y
) { }
以上三個函數(shù)在linux下生成的編譯之后的函數(shù)名為:
_Z4funcv
_Z4funci
_Z4funcic
3. extern “C”淺析
以下在Linux下測試:
c函數(shù): void MyFunc(){} ,被編譯成函數(shù): MyFunc c++函數(shù): void MyFunc(){} ,被編譯成函數(shù): _Z6Myfuncv 由于c++中需要支持函數(shù)重載,所以c和c++中對同一個函數(shù)經(jīng)過編譯后生成的函數(shù)名是不相同的,這就導致了一個問題.
如果在c++中調(diào)用一個使用c語言編寫模塊中的某個函數(shù),那么c++是根據(jù)c++的名稱修飾方式來查找并鏈接這個函數(shù),那么就會發(fā)生鏈接錯誤,以上例,c++中調(diào)用MyFunc函數(shù),在鏈接階段會去找Z6Myfuncv,結果是沒有找到的,因為這個MyFunc函數(shù)是c語言編寫的,生成的符號是MyFunc。
那么如果想在c++調(diào)用c的函數(shù)怎么辦?
extern "C"的主要作用就是為了實現(xiàn)c++代碼能夠調(diào)用其他c語言代碼。加上extern "C"后,這部分代碼編譯器按c語言的方式進行編譯和鏈接,而不是按c++的方式。
總結
以上是生活随笔 為你收集整理的C/C++学习之路: C++对C的扩展 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。