C++ primer第六章6.5函數的學習 之特殊用途的語言特性

6.5.1 默認實參

  • 將反復出現的數值稱為函數的默認實參,調用含有默認實參的時候可以包含該實參也可以不包含
  • 比如程序打開頁面會有一個默認的寬高,如果用戶不喜歡也允許用戶自由指定與默認數值不同的數值,具體例子如下圖所示
typedef string::size_type sz;
string screen(sz ht = 24,sz wid = 80,char backgrnd = ' ');
  • 默認實參作為形參的初始化數值出現在形參列表中,可以為一個或者多個形參定義成默認數值,但是,一旦某個形參被賦予了默認數值,這個形參之后的數值也必須有默認數值。

使用默認實參調用函數

  • 如果使用默認的實參,調用函數的時候省略掉該實參就可以了。
  • 如上面提出的screen函數,有三個變量,因此可以使用0、1、2、3個實參調用該函數
typedef string::size_type sz;
string screen(sz ht = 24,sz wid = 80,char backgrnd = ' ');
int main(){string window;window = screen();window = screen(1);window = screen(1,2);window = screen(1,2,'&');
}
  • window = screen(,,'&');//錯誤,只可以忽略掉尾部的實參,即左邊的必須補齊定義,只有右邊的才可以缺省
  • 因此,在實際使用的場景中,需要設計形參的順序,將不怎么使用默認形參的變量放在前面

默認實參聲明

  • 對于函數的聲明來講,通常是將其放在頭文件中,并且每個函數聲明一次,但是多次聲明也是合法的。
  • 但是,在給定的作用域中,一個形參只可以賦予一次默認實參。即,函數的后續聲明只能為之前那些沒有默認值的形參添加默認的實參,而且這個形參的左側的所有的形參都有默認值。

默認實參初始值

  • 局部變量不可以作為默認的實參,只要表達式的類型可以轉化為形參所需要的類型,該表達式就可以作為默認的實參。
typedef string::size_type sz;
sz wd = 80;
char def = ' ';
sz ht();
string screen(sz = ht(),sz = wd,char = def);
string window = screen();//調用screen(ht(),80,' ')
  • 用作默認實參的名字,在函數聲明所在的作用域中解析,而這些名字的求值的過程發生在函數調用的時候
void f2(){def = '*';//改變默認的實參的數值sz wd = 100;//隱藏了外層定義的wd,但是沒有改變默認的數值window = screen();//調用了screen(ht(),80,'*')
}
  • 在函數f2的內部改變了def的值,所以對于screen的調用將會傳遞這個更新過的數值。
  • 對于wd,內部聲明的局部變量會隱藏外部的wd,但是該局部變量并不會傳遞給screen的默認的參數

6.5.2 內聯函數和constexpr函數

定義函數的好處

  • 使用函數可以確保行為的統一,每次相關的操作都按照同樣的方式進行
  • 如果需要修改計算的過程,顯然修改函數比先找到等價的表達式所有出現地方再逐一修改更加方便
  • 函數可以被其他應用重復調用,省去了重新編寫開發的代價

壞處

  • 調用函數會比使用表達式慢,因為使用函數要涉及到一系列的工作:調用前保存寄存器、并且在返回的是會恢復;需要拷貝實參,程序需要在不同的新的位置上繼續執行。

內聯函數可以避免函數的調用所帶來的開銷

  • 將函數指定為內聯函數,通常是將它在每一個調用點上“內聯地”展開,將會減少函數運行使得開銷。
  • 在函數的前面加上關鍵字inline就可以將其聲明稱為內聯函數

inline const string &shorterString(const string &s1,const string &s2){return s1.size() < s2.size() ? s1 : s2;
}
  • 內聯說明只是向編譯器發出一個請求,編譯器可以選擇忽略這個請求
  • 內聯機制一般用于優化規模較小,流程直接、頻繁調用的函數,很多編譯器都不支持內聯遞歸函數,如果程序很大的話,也不大可能在調用點內聯的展開

constexpr函數

  • constexpr是指可以用于常量表達式的函數。
  • 其函數的返回類型以及所有的形參的類型都得是字面值類型,而且函數體中必須有且只有一個return語句。
constexpr int new_sz() {return 42;}
constexpr int foo = new_sz(); //正確,foo是一個常量的表達式
  • 把new_sz定義成無參數的constexpr函數,因為編譯器能在程序編譯的時候驗證new_sz函數返回的是常量的表達式,所以可以使用new_sz來初始化foo函數。
  • 執行該初始化任務的時候,編譯器把constexpr函數的調用替換成其結果數值。為了能在編譯的過程中隨時展開,constexpr會被隱式轉變為內聯函數。
  • constexpr函數體內也可以包含其他語句,只要這些語句在運行的時候不執行任何操作就可以。例如,constexpr函數內可以有空的語句、類型別名以及using聲明。
  • 允許constexpr函數的返回值并非一個常量。
//如果arg是常量的表達式,則scale(arg)也是常量的表達式
constexpr size_t scale(size_t cnt){return new_sz() * cnt;//當scale的實參是常量表達式的時候,返回的數值也是常量的表達式;反之不然;
}
int main(){int arr[scale(2)];//正確,scale(2)是常量的表達式int i = 2;int a2[scale(i)];//錯誤,scale(i)不是常量的表達式
}

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

  • 和其他函數不一樣,內聯函數和constexpr函數可以在程序中多次定義。畢竟,編譯器要想展開函數僅僅有函數的聲明是不夠的,還需要函數的定義。但是對于某個給定的內聯函數或者constexpr函數來說,他的多個定義必須完全一致,因此通常將他們定義在頭文件中。

6.5.3 調試幫助

  • C++有些時候會用到一些類似于頭文件保護的技術,從而可以有選擇的執行代碼。
  • 基本思想是,程序包含一些用于調試的代碼,但是這些代碼只會在開發程序的時候使用,當程序完準備上線的時候,需要先屏蔽代碼,這就用到了兩項預先處理的功能:assert和NDEBUG

assert預處理宏

  • 預處理宏就是一個預處理變量,其行為類似于內聯函數
  • assert使用一個表達式作為它的條件 assert(expr);? 首先對于expr進行求值,如果表達式為假,assert會輸出信息并且終止程序的執行。如果表達式為真,assert什么也不做
  • assert的頭文件會定義在cassert文件中,因為預處理名字是由預處理器而不是編譯管理,因此可以直接使用預處理名字而不需要提供using聲明。即直接使用assert,而不是std::assert,也不需要為assert提供using的聲明。
  • 和預處理的變量一樣,宏名字在程序內部必須唯一。但是在實際編程的時候,即使沒有包含這個頭文件,也不要使用assert這個名字進行任何相關的變量、函數、其他實體等的命名,因為,很多頭文件都會包含這個cassert,有可能通過其他途徑包含在程序中。
  • assert宏常常用于檢查“不能發生的條件”。例如,一個對于輸入文本進行操作的語句對于輸入的長度有很大的要求,必須大于某一個指定的閾值。這個使用,程序可能會包含一個如下所示的語句? assert(word.size() > threshold );
  • assert都是一種調試程序的輔助手段,但是他不可以替代運行時候的邏輯檢查,也不可以替代程序本身應該包含的錯誤檢查

NDEBUG預處理變量

  • assert的行為依賴于一個名為NDEBUG的預處理變量的狀態。如果定義了NDEBUG,那么assert就什么都不做。默認情況下是沒有定義這個NDEBUG的,因此assert會執行運行檢查。
  • 使用#define NDEBUG 或者使用命令行進行程序編譯的時候,使用 CC -D NDEBUG main.c 這兩者之間是等效的
  • 同理,也可以使用NDEBUG來編寫自己的條件調試的代碼,如果NDEBUG沒有定義,那么將會執行#ifdef和#endif之間的代碼;如果定義了NDEBUG,那么這些代碼將會被忽略掉
  • #define NDEBUG
#ifdef NDEBUG// __func__是編譯器定義的一個局部靜態變量,用于存放函數的名字cerr << __func__ << ": array size is " << size << endl;
#endif
  • __func__是一個const char的一個靜態數組,用于存放函數的名字

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

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

相關文章

Android設計模式之——Builder模式

一、介紹 Builder模式是一步一步創建一個復雜對象的創建型模式&#xff0c;它允許用戶在不知道內部構建細節的情況下&#xff0c;可以更精細的控制對象的構造流程。該模式是為了將構建復雜對象的過程和它的部件解耦&#xff0c;使得構建過程和部件的表示隔離開來。 因為一個復…

c++后端開發書籍推薦

推薦書籍: 略讀80% 精讀50% C&#xff1a; C Primer Plus C和指針&#xff08;入門書 不只是指針&#xff09; C陷阱與缺陷&#xff08;宏相關&#xff09; C專家編程 C&#xff1a; 有專門的視頻 C primer C程序設計原理與實踐&#xff08;c之父寫的 入門經典&#xff09; Ef…

C++ primer第六章6.6函數匹配

函數的匹配 當重載函數的形參數量相等以及某些形參的類型可以由其他的類型轉化得來的時候&#xff0c;對于函數的匹配就會變得很難 確定候選函數和可行函數 函數匹配的第一步就是選定本次調用對應的重載函數集&#xff0c;集合中的函數稱為候選函數。候選函數具有兩個特征&am…

Android設計模式之——原型模式

一、介紹 原型模式是一個創建型的模式。原型二字表明了該模型應該有一個樣板實例&#xff0c;用戶從這個樣板對象中復制出一個內部屬性一致的對象&#xff0c;這個過程也就是我們俗稱的“克隆”。被復制的實例就是我們所稱的“原型”&#xff0c;這個原型也是可定制的。原型模…

C++ primer第六章6.7函數指針

函數指針 函數指針指向的是函數而不是對象。和其他指針一樣&#xff0c;函數指針指向某種特定的類型。函數的類型由他的返回類型和形參類型共同決定&#xff0c;而與函數的名字無關。 //比較兩個string對象的長度 bool lengthCompare(const string &,const string &);…

Android設計模式之——工廠方法模式

一、介紹 工廠方法模式&#xff08;Factory Pattern&#xff09;&#xff0c;是創建型設計模式之一。工廠方法模式是一種結構簡單的模式&#xff0c;其在我們平時開發中應用很廣泛&#xff0c;也許你并不知道&#xff0c;但是你已經使用了無數次該模式了&#xff0c;如Android…

C++ primer第十八章 18.1小結 異常處理

18.1 異常處理 異常處理機制&#xff0c;允許程序獨立開發的部分能夠在運行的時候出現的問題進行通信并且做出相應的處理&#xff0c;異常的處理使得我們可以將問題的檢測和處理分離開來。程序的一部分負責檢測問題的出現&#xff0c;然后將解決這個問題的任務傳遞給程序的另一…

淺談equals與==

一、前言 示例代碼&#xff1a; public static void main(String[] args) throws IOException {String str1 new String("hello");String str2 new String("hello");String str3 "cde";String str4 "cde";int i1 3;int i2 3;In…

針對C++異常的學習

源碼 頭文件 sdf_exception.h #pragma once#include <exception> #include <string>namespace sdf {namespace common{using sdf_error_code_t uint32_t;class SdfException : std::exception{public:explicit SdfException(sdf_error_code_t errorCode) : erro…

Android設計模式之——抽象工廠模式

一、介紹 抽象工廠模式&#xff08;Abstract Factory Pattern&#xff09;&#xff0c;也是創建型設計模式之一。前一節我們已經了解了工廠方法模式&#xff0c;那么這個抽象工廠又是怎么一回事呢&#xff1f;大家聯想一下現實生活中的工廠肯定都是具體的&#xff0c;也就是說…

Android設計模式之——策略模式

一、介紹 在軟件開發中也常常遇到這樣的情況&#xff1a;實現某一個功能可以有多種算法或者策略&#xff0c;我們根據實際情況選擇不同的算法或者策略來完成該功能。例如&#xff0c;排序算法&#xff0c;可以使用插入排序、歸并排序、冒泡排序等。 針對這種情況&#xff0c;…

密碼學在區塊鏈隱私保護中的應用學習

身份隱私保護技術 混淆服務 混淆服務的目的在于混淆消息雙方的聯系&#xff08;如 圖 2 所示&#xff09;。當發送方需要告知接收方消息 M 時&#xff0c; 它會首先用接收方的公鑰 KB 加密 M&#xff0c;并在密文后 附帶真實接收地址 R。為了借助第三方&#xff08;圖 2 中的…

值類型和引用類型的區別

一、定義 引用類型表示你操作的數據是同一個&#xff0c;也就是說當你傳一個參數給另一個方法時&#xff0c;你在另一個方法中改變這個變量的值&#xff0c;那么調用這個方法是傳入的變量的值也將改變。 值類型表示復制一個當前變量傳給方法&#xff0c;當你在這個方法中改變…

面向區塊鏈的高效物化視圖維護和可信查詢論文學習

物化視圖介紹 如何維護物化視圖仍舊是一個開放問題.在關系數據庫中,增量刷新的物化視圖維護策略可劃分為立即維護和延遲維護兩大類.立即維護策略的優點是實現較為簡單,在單數據源下不 存在一致性問題;然而該策略將物化視圖維護過程嵌入到更新事務之中,延長了更新事務的提交時間…

Java基礎知識(一)

一、接口 類描述了一個實體&#xff0c;包括實體的狀態&#xff0c;也包括實體可能發出的動作。 接口定義了一個實體可能發出的動作。但是只是定義了這些動作的原型&#xff0c;沒有實現&#xff0c;也沒有任何狀態信息。 所以接口有點象一個規范、一個協議&#xff0c;是一個…

密碼學數字信封的介紹

對稱密碼和非對稱密碼 對稱密碼&#xff1a;加解密運算非常快&#xff0c;適合處理大批量數據&#xff0c;但其密碼的分發與管理比較復雜非對稱密碼&#xff1a;公鑰和私鑰分離&#xff0c;非常適合密鑰的分發和管理 數字信封的定義 如果將對稱密碼算法和非對稱密碼算法的優點…

Android設計模式之——狀態模式

一、介紹 狀態模式中的行為是由狀態來決定的&#xff0c;不同的狀態下有不同的行為。狀態模式和策略模式的結構幾乎完全一樣&#xff0c;但它們的目的、本質卻完全不一樣。狀態模式的行為是平行的、不可替換的&#xff0c;策略模式的行為是彼此獨立、可相互替換的。用一句話來…

Android設計模式之——責任鏈模式

一、介紹 責任鏈模式&#xff08;Iterator Pattern&#xff09;&#xff0c;是行為型設計模式之一。什么是”鏈“&#xff1f;我們將多個節點首尾相連所構成的模型稱為鏈&#xff0c;比如生活中常見的鎖鏈&#xff0c;就是由一個個圓角長方形的鐵環串起來的結構。對于鏈式結構…

目前基于區塊鏈的檔案防篡改系統的設計如何實現防篡改

架構設計圖 分析 為了保障檔案數據的安全性和隱私性&#xff0c;存儲檔案附件和檔案屬性存儲加密存儲在私有IPFS集群&#xff0c;檔案的IPFS地址和數字指紋存儲在私有區塊鏈上。公有區塊鏈定期存儲和檢查私有區塊鏈最新不可逆區塊的高度和哈希值&#xff0c;以保障私有區塊鏈上…

IPFS的文件存儲模式

IPFS是如何進行文件存儲的 IPFS采用的索引結構是DHT&#xff08;分布式哈希表&#xff09;&#xff0c;數據結構是MerkleDAG&#xff08;Merkle有向無環圖&#xff09; DHT(分布式哈希表) 參考鏈接MerkleDAG&#xff08;Merkle有向無環圖&#xff09; 參考鏈接MerkleDAG功能…