一.inline函數
1.inline的基本特性
被inline修飾的函數被稱為內聯函數。inline函數設計的初衷是為了優化宏的功能,編譯器會在編譯階段對inline函數進行展開。然而需要注意的是,inline對于編譯器而言是一種建議,它通常會展開一些簡短的,非遞歸的函數,遇到較為復雜的函數即使有inline修飾編譯器也會忽略展開
2.與宏的不同之處
宏也會在編譯時展開,對比宏,inline有什么優勢?inline修飾的函數可以調試
有了inline修飾的函數,就不需要再考慮宏中復雜的傳參問題了。例如:
#include<iostream>
using namespace std;
// 實現?個ADD宏函數的常?問題
//#define ADD(int a, int b) return a + b;
//#define ADD(a, b) a + b;
//#define ADD(a, b) (a + b)
// 正確的宏實現
#define ADD(a, b) ((a) + (b))
// 為什么不能加分號?
// 為什么要加外?的括號?
// 為什么要加??的括號?
int main()
{
int ret = ADD(1, 2);
cout << ADD(1, 2) << endl;
cout << ADD(1, 2)*5 << endl;
int x = 1, y = 2;
ADD(x & y, x | y); // -> (x&y+x|y)
return 0;
}
3.使用的注意事項
通常將inline函數定義與聲明都放在頭文件中
不建議將inline的聲明與定義分文件寫,會出現鏈接錯誤。因為inline函數展開后會沒有函數地址
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
} /
/ main.cpp
#include "F.h"
int main()
{
// 鏈接錯誤:?法解析的外部符號 "void __cdecl f(int)" (?f@@YAXH@Z)
f(10);
return 0;
}
地址丟失的底層機制
函數地址的產生條件:
普通函數:編譯器生成函數實體,分配唯一地址,符號表記錄強符號。
inline
函數:若被展開:無獨立函數實體?→ 無地址。
若未展開:生成弱符號實體,鏈接器可丟棄未使用的定義。
分文件編寫的致命缺陷:
調用方(
main.cpp
)無法看到函數體?→ 編譯器必須生成函數調用指令。定義方(F
.cpp
)的inline
函數可能無實體?→ 鏈接器找不到地址。
二.nullptr
nullptr的出現是為了解決C語言中NULL的二義性。C語言中的NULL實際為一個宏,如下:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
C++中的NULL會被定義為常量0,或C中會被定義為無類型指針(void*),這在函數調用時會產生歧義
這里通過重載f()作為例子
#include<iostream>
using namespace std;
void f(int x)
{
cout << "f(int x)" << endl;
}void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
在調用f()時傳入f(NULL)會出現什么情況?
顯然這里的調用結果與預期不符。
那么嘗試將NULL強轉為int*會出現什么情況?
因此我們使用nullptr來規避這樣的情況
nullptr是一種特殊類型的字面量,它可以隱式轉換為各種類型的指針類型,而無法被轉換為整型