函數重載、引用再探、內聯函數

文章目錄

  • 函數重載
  • 為什么C++支持重載,C語言不支持呢?
  • extern “C”
  • 引用再探
    • 引用的特性
    • 引用的使用場景
    • 引用和指針
    • 引用和指針的不同點:
  • 內聯函數
    • 什么是內聯函數?
    • 內聯函數的特性
    • 內聯函數的好處
    • 類的內聯成員函數的聲明
    • 內聯函數的使用
  • constexpr函數
    • 概念
    • 特征
    • 內聯函數和constexpr函數放在頭文件內


函數重載

在同一個作用域下,對于相同的函數名,函數的參數類型不同,參數順序不同,參數的個數不同, 都可以形成函數的重載參數名不同,返回值不同不形成重載)

函數的重載主要用于處理功能相同,形參類型不同的數據。

void test(int i, int j)
{cout << "test" << endl;
}void test(double i, int j) // 類型不同
{cout << "test" << endl;
}void test(int j, double i) // 順序不同
{cout << "test" << endl;
}void test(double i, int j, int k) // 個數不同
{cout << "test" << endl;
}

為什么C++支持重載,C語言不支持呢?

因為windows對函數重載的處理更加復雜,所以這里用linux下的gcc和g++來看更加直觀。

首先我們要知道,鏈接器看到有函數被調用的時候,就會到符號表中去查找對應的函數名,來獲取函數的地址,再鏈接到一起

先看C語言是怎么處理的
在這里插入圖片描述
在這里插入圖片描述
通過反匯編我們可以看到,C語言并沒有對函數名進行處理,也就是說無論我們參數的個數,參數的類型,參數的順序怎么修改,它只認函數名,如果出現了第二個相同函數名的,就算重定義。

下面再看C++的:
在這里插入圖片描述
在這里插入圖片描述
這里可以看到,C++對函數名進行了處理,函數以_Z4開頭,接著是函數名,最后是所有參數的縮寫。

_Z是所有函數的前綴,4是函數名的字符個數,例如第一個_Z4testii則代表函數名為test,具有四個字符,參數類型縮寫分別是ii。

這也就是為什么返回值不同和參數名不構成重載的原因,它們不被作為對函數特征的處理。C++正是通過這種函數名修飾規則來實現函數的重載。


extern “C”

有時候我們在使用C++的時候,對于某些函數,想讓它按照C的風格來編譯,那么就在函數前加extern “C”,意思是告訴編譯器,將該函數按照C語言規則來編譯。

在這里插入圖片描述


引用再探

引用的特性

  1. 引用在定義的時候必須初始化(因為引用是某個對象的別名,所以必須初始化)
  2. 一個對象可以有多個引用
  3. 一旦引用一個實體,就不能再引用別的實體(有點類似指針的頂層const)

引用的使用場景

  1. 作為參數
struct A
{int arr[1000000];
};void test(A& s1)
{}

假設我們存在一個超級大的結構體,如果我們直接將結構體傳過去的話,會產生一個臨時變量來將這個結構體拷貝到形參中,這是極大的開銷,但如果我們使用引用的話,傳的只是一個別名而已,所有的操作還是在結構體本身上進行的,但是需要注意的和上面一樣,如果我們要傳遞一個常量,就必須要在引用前加上const。

struct A
{int arr[1000000];
};void test (const A& s1)
{}
int main(int argc, char const *argv[])
{const A a = {10,324,32};test(a);return 0;
}
  1. 作為返回值
int& Add(int a, int b) 
{int c = a + b;return c;
}int main()
{int& ret = Add(1, 2);Add(3, 4);cout << ret << endl;return 0;
}

對于這樣一個代碼,我們可能第一眼覺得ret會是3。

在這里插入圖片描述

但是其實是7。

因為我們返回的是c的一個引用,但是c只存在于調用時的那個棧幀,調用結束后那個棧幀就會被銷毀,雖然銷毀后數據不會被清空,但是那片區域的訪問權限就會被放開,有可能會被下次調用的函數使用,也有可能會被其他的一個操作給使用,所以這是一種極為不安全的行為。

上面的7是第二次調用后修改了c的值。

所以,如果需要引用作為返回值,就必須保證出了函數作用域,返回的對象沒有歸還給系統,仍然存在。

以值作為參數或者返回值時,在傳參和返回的時候,都會傳遞或返回原變量的一個臨時的拷貝,這樣的效率是非常低下的,尤其是數據特別大的時候,但如果使用引用作為參數的話,就不會有這樣的問題。

引用和指針

語法概念上:引用是對象的一個別名,沒有獨立的空間,和其引用的實體共用一個空間。

但我們發現,引用其實和指針很像,它更像一個頂層const的指針,所以我們可以進入反匯編看看他們之間有沒有關系

int main()
{int x = 5;int& y = x;int* z = &x;return 0;
}

在這里插入圖片描述
反匯編下我們可以看到,指針和引用在匯編下的實現是一模一樣的。

所以我們可以得出一個結論:引用是按照指針來實現的,在指針的基礎上又給他封裝了新的功能。

引用和指針的不同點:

  1. 引用在定義時必須初始化,指針沒有要求
  2. 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型 實體
  3. 沒有NULL引用,但有NULL指針
  4. 在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位平臺下占 4個字節)
  5. 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小
  6. 有多級指針,但是沒有多級引用
  7. 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理
  8. 引用比指針使用起來相對更安全

內聯函數

什么是內聯函數?

用inline關鍵字修飾的函數就是內聯函數,在編譯時編譯器會將函數的代碼在調用內聯函數的地方展開,減去了函數壓棧的開銷,提升程序運行的效率(犧牲空間換取時間)。
在這里插入圖片描述
例如這樣一個簡單的代碼
如果我們直接調用它
在這里插入圖片描述
在匯編下可以看到,他會創建一個新的棧幀,將參數3,4壓棧,然后計算完再返回結果

而如果在函數前面加上inline使其變為內聯函數
在這里插入圖片描述
這時再看,就會發現它直接把函數的代碼在調用處直接展開,不會再創建新的棧幀。

內聯函數的特性

  1. 內聯函數是一種用空間換時間的做法,省去了創建棧幀和壓棧的開銷,但也因此代碼很復雜和具有循環或遞歸之類的函數不適合作為內聯函數,就算聲明為內聯函數編譯器也會自動將其忽略。
  2. 內聯函數不能聲明和定義分離,因為一旦聲明為內聯函數,在調用的時候就會直接展開,沒有了函數的地址,就無法將其鏈接到定義的部分。

值得一提的是,內聯函數與C語言中的宏函數有些類似,雖然宏的性能不錯,但是因為宏缺乏類型的安全檢查和無法調試(在預處理階段就進行了宏替換),在C++中宏函數被內聯函數替代,宏常量定義被const取代。

內聯函數的好處

  1. 較之等價的表達式更易于閱讀
  2. 可以被其他應用重復利用,省去了重新編寫的代價
  3. 如需修改計算過程,顯然修改函數比先找到等價表達式所有出現的地方再逐一修改更容易。

類的內聯成員函數的聲明

我們可以在類內把 inline 作為聲明的一部分顯式地聲明成員函數,同樣的,也能在類的外部用 inline 關鍵字修飾函數的定義(當然在聲明和定義的地方同時說明 inline 也是合法,只是沒有必要)。

內聯函數的使用

  • 濫用內聯將導致程序變得更慢;
  • 最好不要內聯超過 10 行的函數;
  • 謹慎對待析構函數,析構函數往往比其表面看起來要更長,因為有隱含的成員和基類析構函數被調用;
  • 內聯那些包含循環或 switch 語句的函數常常是得不償失 (除非在大多數情況下,這些循環或 switch 語句從不被執行);
  • 有些函數即使聲明為內聯的也不一定會被編譯器內聯:比如虛函數和遞歸函數就不會被正常內聯。
    • 通常,遞歸函數不應該聲明成內聯函數。(遞歸調用堆棧的展開并不像循環那么簡單, 比如遞歸層數在編譯時可能是未知的,大多數編譯器都不支持內聯遞歸函數)。
    • 虛函數內聯的主要原因則是想把它的函數體放在類定義內,為了圖個方便,亦或是當作文檔描述其行為,比如精短的存取函數。

constexpr函數

概念

能用于常量表達式的函數

特征

  1. 函數的返回類型及所有形參的類型都得是字面值類型
  2. 函數體中必須有且只有一條return語句
  3. 編譯器把對constexpr函數的調用替換成其結果值(constexpr函數被隱式地指定為內聯函數)
  4. 函數體內允許包含 運行時不執行任何操作的語句
  5. 允許返回一個非常量,應用時編譯器會進行檢查。(constexpr不一定返回常量表達式)

內聯函數和constexpr函數放在頭文件內

和其他函數不同,內聯函數和constexpr可以在程序中多次定義(每一次展開就是一次定義)。但多個定義必須完全一致,基于這個原因,內聯函數和constexpr函數通常定義在頭文件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443826.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443826.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443826.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

類的概念、成員函數的定義方式、類的訪問控制和封裝、類的大小、this指針

文章目錄類的概念structclassclass和struct的區別是什么呢&#xff1f;類中成員函數的兩種定義方式聲明和定義都在類中聲明和定義分離類的訪問控制和封裝類的封裝特性類的大小結構體內存對齊規則類的存儲方式this指針類的概念 在C中&#xff0c;類可以說是最重要的東西&#x…

jQuery實現兩個列表框的值之間的互換:

jQuery實現兩個列表框的值之間的互換&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath reque…

類的6個默認成員函數:構造函數、析構函數、拷貝構造函數、重載運算符、三/五法則

文章目錄6個默認成員函數構造函數概念默認構造函數的類型默認實參概念默認實參的使用默認實參聲明全局變量作為默認實參某些類不能依賴于編譯器合成的默認構造函數第一個原因第二個原因第三個原因構造函數初始化構造函數里面的“”是初始化嗎&#xff1f;為什么要使用列表初始化…

C++ 類的知識 | 構造函數再探、匿名對象、友元函數、內部類、類的const成員、類的static成員

文章目錄構造函數再探以下代碼共調用多少次拷貝構造函數委托構造函數概念形式匿名對象友元友元的聲明友元類令成員函數作為友元函數重載和友元注意內部類特性類的const成員可變數據成員類的static成員概念關于static靜態成員的類內初始化靜態成員能用于某些普通成員不能的場景構…

截取全部數值字符并將其轉化為數值類型

功能 從name中找出全部數值字符&#xff0c;之后將name&#xff08;string類&#xff09;轉為d&#xff08;double類&#xff09; 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #i…

替換string中的部分字符

功能 向函數fun中傳入三個參數&#xff1a;將s中所有oldval替換為newval 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #include <array> using namespace std;void fun(str…

順序容器(vector、list、string、deque、forward_list)及迭代器、容器適配器

文章目錄概述所有容器都支持的操作迭代器迭代器支持的操作迭代器支持的算術運算容器類型size_typeiterator 和 const_iterator容器定義和初始化拷貝初始化順序容器獨有的構造函數&#xff08;array除外&#xff09;array的初始化與內置數組類型的區別6種初始化方法&#xff08;…

jQuery實現表格隔行換顏色:

jQuery實現表格各行換顏色&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack處理中綴表達式【+、-、*、/、()】

文章目錄題目描述思路使用getline()存儲輸入的字符串邊讀取邊壓棧完整代碼題目描述 使用stack處理括號化的表達式。當你看到一個左括號&#xff0c;將其記錄下來。當你在一個左括號之后看到一個右括號&#xff0c;從stack中pop對象&#xff0c;直至遇到左括號&#xff0c;將左括…

原地置換法尋找數組中重復的數

文章目錄題目描述代碼實現題目描述 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 輸入&#xff1a; [2,…

二維數組的查找

文章目錄題目描述思路注意代碼題目描述 在一個 n * m 的二維數組中&#xff0c;每一行都按照從左到右遞增的順序排序&#xff0c;每一列都按照從上到下遞增的順序排序。請完成一個高效的函數&#xff0c;輸入這樣的一個二維數組和一個整數&#xff0c;判斷數組中是否含有該整數…

雙指針

文章目錄題目描述思路注意代碼實現題目描述 請實現一個函數&#xff0c;把字符串 s 中的每個空格替換成"%20"。 示例 1&#xff1a; 輸入&#xff1a;s “We are happy.” 輸出&#xff1a;“We%20are%20happy.” 限制&#xff1a; 0 < s 的長度 < 10000 思…

Springmvc,Spring MVC文件上傳

Springmvc文件上傳&#xff1a; 1.代碼截圖如下&#xff1a; 2.UploadController.java: package cn.csdn.controller;import java.io.File;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller; import org.springframework.ui.…

倒序輸出鏈表

文章目錄題目描述思路遞歸法棧題目描述 輸入一個鏈表的頭節點&#xff0c;從尾到頭反過來返回每個節點的值&#xff08;用數組返回&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;head [1,3,2] 輸出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 鏈表長度 < 10000 思…

插入迭代器、流迭代器、反向迭代器、移動迭代器

文章目錄前言插入迭代器inserterfront_inserterback_inserteriostream迭代器istream_iterator 讀取輸入流istream_iterator允許使用懶惰求值ostream_iterator操作反向迭代器reverse_iterator的base成員函數前言 除了為每個容器定義的迭代器之外&#xff0c;標準庫在頭文件iter…

泛型算法(lambda表達式、function類模板、bind函數適配器、迭代器類別、鏈表數據結構獨有的算法)

文章目錄概念find()函數迭代器令算法不依賴于容器但算法依賴于元素類型的操作算法永遠不會執行容器的操作只讀算法accumulate()函數從兩個序列中讀取元素&#xff08;equal函數為例&#xff09;迭代器作為參數形成兩個序列equal()寫容器元素的算法概念fill()fill_n()插入迭代器…

jsp,div 限制字數,超出部分用省略號代替

1.我是用struts2標簽做的&#xff1a;如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% taglib prefix"s" uri"/struts-tags"%> <%String path request.getContext…

C++之關聯容器

文章目錄概述及類型mapsetpair類型概念初始化默認初始化提供初始化器允許的操作可以創建一個pair類的函數可以作為容器的類型關聯容器迭代器概念map的迭代器set的迭代器是const的初始化map and setmultimap and multiset關聯容器的操作額外的類型別名關聯容器和算法刪除元素添加…

動態內存、智能指針(shared_ptr、unique_ptr、weak_ptr)、動態數組

文章目錄三種對象的分類三種內存的區別動態內存概念智能指針允許的操作智能指針的使用規范new概念內存耗盡/定位new初始化默認初始化直接初始化值初始化delete概念手動釋放動態對象空懸指針shared_ptr類格式獨有的操作make_shared函數shared_ptr的計數器通過new用普通指針初始化…

動態數組的簡單應用

文章目錄連接兩個字符串字面常量題目注意代碼輸出結果處理輸入的變長字符串題目注意代碼連接兩個字符串字面常量 題目 連接兩個字符串字面常量&#xff0c;將結果保存在一個動態分配的char數組中。重寫&#xff0c;連接兩個標準庫string對象。 注意 使用頭文件cstring的str…