淺析c++函數(shù)參數(shù)和返回值
c++函數(shù)參數(shù)和返回值
c++一直以來(lái)是一個(gè)關(guān)注效率的代碼,這樣關(guān)于函數(shù)的參數(shù)傳遞和返回值的接收,是重中之重。下文提供了一些個(gè)人的見(jiàn)解。
函數(shù)存儲(chǔ)位置
函數(shù)參數(shù)在編譯期展開(kāi),目前各平臺(tái)的編譯期均有不同。
| 名稱 | 存儲(chǔ)位置 |
|---|---|
| 函數(shù)名稱和邏輯 | 代碼段存儲(chǔ) |
| 函數(shù)參數(shù)和返回值 | 棧中或者寄存器(64位會(huì)有6個(gè)寄存器使用) |
| new malloc 的變量 | 堆 |
函數(shù)參數(shù)入棧順序
微軟有幾種編譯期屬性,用來(lái)定義函數(shù)參數(shù)的順序和堆棧。
| 關(guān)鍵字 | 堆棧清理 | 參數(shù)傳遞 |
|---|---|---|
| __cdecl | 調(diào)用方 | 在堆棧上按相反順序推送參數(shù)(從右到左) |
| __clrcall | 不適用 | 按順序?qū)?shù)加載到 CLR 表達(dá)式堆棧上(從左到右)。 |
| __stdcall | 被調(diào)用方 | 在堆棧上按相反順序推送參數(shù)(從右到左) |
| __fastcall | 被調(diào)用方 | 存儲(chǔ)在寄存器中,然后在堆棧上推送 |
| __thiscall | 被調(diào)用方 | 在堆棧上推送;存儲(chǔ)在 ECX 中的 this 指針 |
| __vectorcall | 被調(diào)用方 | 存儲(chǔ)在寄存器中,然后按相反順序在堆棧上推送(從右到左) |
所以直接在函數(shù)參數(shù)上,調(diào)用表達(dá)式和函數(shù)來(lái)回去值的話,非常危險(xiǎn)
初始化列表
class Init1
{
public:
void Print()
{
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
}
int c, a, b;
};A這個(gè)類,可以通過(guò) A a{1,2,3}; 來(lái)初始化對(duì)象。
看著很美好,但是有幾個(gè)問(wèn)題需要注意。
參數(shù)是的入棧順序是跟著類的屬性的順序一致, 當(dāng)前是 c, a, b;
int i = 0;
Init1 a = {i++, i++, i++};
a.Print();當(dāng)我如此調(diào)用的時(shí)候,得到的返回值是 1 2 0i++的執(zhí)行順序是從左到右,跟函數(shù)調(diào)用順序無(wú)關(guān)。 另外不能有 構(gòu)造函數(shù)
class Init1
{
public:
Init1(int ia, int ib, int ic)
{
std::cout << "construct" << std::endl;
a = ia;
b = ib;
c = ic;
}
Init1(const Init1& other)
{
std::cout << "copy " << std::endl;
a = other.a;
b = other.b;
c = other.c;
}
void Print()
{
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
}
int c, a, b;
};當(dāng)我添加了構(gòu)造函數(shù)的時(shí)候。 用下面代碼測(cè)試。會(huì)得到兩種結(jié)果
void Test_InitilizeList()
{
int i = 0;
//Init1 a = { i++, i++, i++ }; // 0 1 2
Init1 a(i++, i++, i++); // 2 1 0
a.Print();
}函數(shù)的返回值
函數(shù)返回值的聲明周期在函數(shù)體內(nèi)。
用參數(shù)引用來(lái)返回
class Result
{
public:
int result;
};
void GetResult(Result& result) ...優(yōu)點(diǎn):
- 效率最高,因?yàn)榉祷刂档膶?duì)象在函數(shù)體外構(gòu)造,可以一直套用, 可以一處構(gòu)造,一直使用。
- 安全,可以定義對(duì)象,并不用new或者malloc, 沒(méi)有野指針困擾。
缺點(diǎn): - 代碼可讀性低,不夠優(yōu)美
- 無(wú)法返回nullptr. 一般在 Result 中定義一個(gè); 用來(lái)表示一個(gè)空對(duì)象。
- 容易賦值到一個(gè)臨時(shí)對(duì)象中,當(dāng)調(diào)用
GetResult({1})會(huì)賦值到一個(gè) 臨時(shí)的 Result 對(duì)象中,拿不到返回值。正常來(lái)說(shuō)也不會(huì)這樣做。
返回一個(gè)參數(shù)指針
class Result
{
public:
int result;
};
Result* GetResult() ...優(yōu)點(diǎn):
- 簡(jiǎn)潔明了
- 參數(shù)傳遞快速
缺點(diǎn): - 指針如果在 函數(shù)內(nèi) static 需要考慮多線程。 如果是 new 出來(lái)的,多次調(diào)用效率不高
- 指針無(wú)法重復(fù)使用,(可以用 std::share_ptr 增加對(duì)象池來(lái)解決問(wèn)題。但會(huì)引入新的復(fù)雜度。)
- 需要考慮釋放的問(wèn)題
返回一個(gè)對(duì)象
class Result
{
public:
int result;
};
Result GetResult() ...優(yōu)點(diǎn):
- 沒(méi)有內(nèi)存泄露的風(fēng)險(xiǎn)
- 簡(jiǎn)潔明了
缺點(diǎn): - 但有個(gè)別編譯期優(yōu)化選項(xiàng)問(wèn)題,會(huì)導(dǎo)致一次構(gòu)造兩次拷貝, 第一次是函數(shù)體內(nèi)對(duì)象向返回值拷貝,第二次是 返回值拷貝給外面接收參數(shù)的。
- 開(kāi)啟編譯期優(yōu)化選項(xiàng),并且是 在 return Result 的時(shí)候構(gòu)造返回對(duì)象,才能優(yōu)化。
總結(jié)
一般如果是 簡(jiǎn)單結(jié)構(gòu)體,用 返回一個(gè)臨時(shí)對(duì)象的方式解決。
如果使用 返回一個(gè)參數(shù)指針,一般改成返回一個(gè)id,用一個(gè)manager來(lái)管理內(nèi)存機(jī)制?;蛘?共享內(nèi)存,內(nèi)存池來(lái)解決內(nèi)存泄露后續(xù)的問(wèn)題
用 參數(shù)引用來(lái)返回的話,一般會(huì)這么定義 int GetResult(Result& result) 函數(shù)返回值,用來(lái)返回狀態(tài)碼,真正的數(shù)據(jù),放到 result 中。
函數(shù)的幾種變體
inline 函數(shù)
- inline 函數(shù)是內(nèi)聯(lián)函數(shù),是編譯期優(yōu)化的一種手段,一般是直接展開(kāi)到調(diào)用者代碼里,減少函數(shù)堆棧的開(kāi)銷。
- inline 標(biāo)識(shí)只是建議,并不是一定開(kāi)啟內(nèi)聯(lián)。
- 函數(shù)比較復(fù)雜或者遞歸有可能編譯期不展開(kāi)。
- dll 導(dǎo)出的時(shí)候,可以不用加導(dǎo)出標(biāo)識(shí),會(huì)直接導(dǎo)出到目標(biāo)處。
- inline 在msvc的平臺(tái),只要實(shí)現(xiàn)頭文件中,加不加內(nèi)聯(lián)是一樣的. (警告頂級(jí)調(diào)到最高/Wall, 不加inline標(biāo)識(shí)的函數(shù)會(huì)提示,未使用的內(nèi)聯(lián)函數(shù)將被刪除。)
- inline 函數(shù)比全局函數(shù)更快,但是全局函數(shù)無(wú)法定義在頭文件中(會(huì)報(bào)多重定義函數(shù)。)所以一般用class 包一層 static inline 函數(shù),用來(lái)寫工具類。
函數(shù)對(duì)象
class A {
public :
int value;
int operator() (int val) {
return value + val;
}
}上述代碼是一個(gè)函數(shù)對(duì)象,重載operator()得到一個(gè)函數(shù)對(duì)象。int a = A{10}(1) 會(huì)返回11, 顯示構(gòu)造了一個(gè)A{value=10}的對(duì)象,然后調(diào)用重載函數(shù)operator(), 返回 10 + 1 = 11
上述代碼因?yàn)槭窃陬^文件實(shí)現(xiàn)的,所以編譯期會(huì)自動(dòng)把operator()函數(shù)當(dāng)成inline函數(shù),執(zhí)行效率很高。
lambda 函數(shù)
lambda 其實(shí)就是一個(gè)函數(shù)對(duì)象,會(huì)在編譯期展開(kāi)成一個(gè)函數(shù)對(duì)象體。
到此這篇關(guān)于c++函數(shù)參數(shù)和返回值的文章就介紹到這了,更多相關(guān)c++函數(shù)參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++11新特性之智能指針(shared_ptr/unique_ptr/weak_ptr)
這篇文章主要介紹了C++11新特性之智能指針,包括shared_ptr, unique_ptr和weak_ptr的基本使用,感興趣的小伙伴們可以參考一下2016-08-08
用C# 實(shí)現(xiàn)鼠標(biāo)框選效果的實(shí)現(xiàn)代碼
本篇文章是對(duì)用C#實(shí)現(xiàn)鼠標(biāo)框選效果的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
詳解C 語(yǔ)言項(xiàng)目中.h文件和.c文件的關(guān)系
這篇文章主要介紹了詳解C 語(yǔ)言項(xiàng)目中.h文件和.c文件的關(guān)系的相關(guān)資料,需要的朋友可以參考下2017-05-05
C++實(shí)現(xiàn)簡(jiǎn)單推箱子小游戲
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單推箱子小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08
C++實(shí)現(xiàn)LeetCode(73.矩陣賦零)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(73.矩陣賦零),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言實(shí)現(xiàn)二叉樹鏈?zhǔn)浇Y(jié)構(gòu)的示例詳解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)二叉樹鏈?zhǔn)浇Y(jié)構(gòu)的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定的幫助,需要的可以參考一下2022-11-11
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之棧和隊(duì)列的實(shí)現(xiàn)及應(yīng)用
棧和隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),只規(guī)定了性質(zhì),并沒(méi)有規(guī)定實(shí)現(xiàn)方式。本文將以順序結(jié)構(gòu)實(shí)現(xiàn)棧,鏈表方式實(shí)現(xiàn)隊(duì)列,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下吧2022-08-08

