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