item8 nullptr
優(yōu)先于 0 和 NULL
總結(jié):
(1) 0: 0 是 int 型
, 編譯器將 應(yīng)該出現(xiàn)指針卻出現(xiàn)0
的位置上的0 勉強(qiáng)轉(zhuǎn)換為 空指針 (void*)0
(2) NULL: C++ 中可能被編譯器底層實現(xiàn)為 整型 (int/long)
Note: C
中, 還可能被編譯器底層實現(xiàn)為空指針 (void*)0
(3) nullptr: 可 隱式轉(zhuǎn)換
為所有/任意類型
的裸指針類型(進(jìn)而可以被 智能指針形參 接收: 構(gòu)造出 智能指針)
, 不會被視為
任何整型
nullptr 的類型是 std::nullptr_t
std::nullptr_t 可隱式轉(zhuǎn)換為 all pointer types
分析: 相對于 0/NULL,
1 nullptr 可避免 重載解析
時的混淆/錯誤
0/NULL 作實參
, 調(diào)重載函數(shù) f(int/bool/void*)
時, 必然調(diào)用 f(int), 而非 f(void*) / 行為不確定
原因: 若 NULL 實現(xiàn)為 0L, 0L(long) -> int/bool/void*
被編譯器認(rèn)為同樣好
void f(int); // three overloads of f
void f(bool);
void f(void*);
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
// f(int). Never calls f(void*)
f(nullptr); // calls f(void*) overload
2 nulptr 提高了代碼的 清晰性
, 尤其與 auto
結(jié)合時
(1) 用 0 看不出 函數(shù)的返回類型
auto result = findRecord( /* arguments */ );
if (result == 0)
{
…
}
(2) 用 0 能看出函數(shù)的返回類型
必然為指針類型
auto result = findRecord( /* arguments */ );
if (result == nullptr)
{
…
}
3 nullptr 在模板
中優(yōu)勢最大
lock + call
int f1(std::shared_ptr<Widget> spw); // call these only when
double f2(std::unique_ptr<Widget> upw); // the appropriate
bool f3(Widget* pw); // mutex is locked
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxGuard = std::lock_guard<std::mutex>;
…
{
MuxGuard g(f1m); // lock mutex for f1
auto result = f1(0); // pass 0 as null ptr to f1
} // unlock mutex
…
{
MuxGuard g(f2m); // lock mutex for f2
auto result = f2(NULL); // pass NULL as null ptr to f2
} // unlock mutex
…
{
MuxGuard g(f3m); // lock mutex for f3
auto result = f3(nullptr); // pass nullptr as null ptr to f3
} // unlock mutex
前2個沒用 nullptr, 但也正確
Extract 1個模板來去重
, 將 指針實參
抽象為 指針類型 PtrType
=> 0/NULL 作實參 推斷出錯誤的類型 int/int 或 long
, int/int 或 long 再試圖傳給 智能指針型形參的函數(shù) f1/f2
時, 類型錯誤
, 編譯報錯
nullptr 的類型是 std::nullptr_t => PtrType 為 std::nullptr_t, 再傳給 f3時, std::nullptr_t 隱式轉(zhuǎn)換為 Widget*
template<typename FuncType,
typename MuxType,
typename PtrType>
auto
lockAndCall(FuncType func,
MuxType& mutex, // mutex 以引用傳遞
PtrType ptr) -> decltype( func(ptr) )
{
MuxGuard g(mutex);
return func(ptr);
}
auto result1 = lockAndCall(f1, f1m, 0); // error!
…
auto result2 = lockAndCall(f2, f2m, NULL); // error!
…
auto result3 = lockAndCall(f3, f3m, nullptr); // fine
解決: 1/2用 nullptr 調(diào)用
例1: 0/NULL 能直接傳給 智能指針: 先隱式轉(zhuǎn)換為空指針, 再構(gòu)造出智能指針
#include <mutex>
#include <memory>
class Widget
{
};
int f1(std::shared_ptr<Widget> spw) // call these only when
{
return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
return 0.0;
}
bool f3(Widget* pw) // mutex is locked
{
return false;
}
int main()
{
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxGuard = std::lock_guard<std::mutex>;
{
MuxGuard g(f1m); // lock mutex for f1
auto result = f1(0); // pass 0 as null ptr to f1
} // unlock mutex
{
MuxGuard g(f2m); // lock mutex for f2
auto result = f2(NULL); // pass NULL as null ptr to f2
} // unlock mutex
{
MuxGuard g(f3m); // lock mutex for f3
auto result = f3(nullptr); // pass nullptr as null ptr to f3
} // unlock mutex
}
例2: 0/NULL 不能
通過 1次模板實參推斷后
, 再通過隱式轉(zhuǎn)換
為空指針而間接傳給智能指針
原因: 函數(shù)模板實例化
過程中, 函數(shù)模板實參推斷后
, 只能按推斷出的類型傳遞給另一函數(shù), 不再進(jìn)行隱式轉(zhuǎn)換
#include <mutex>
#include <memory>
class Widget
{
};
int f1(std::shared_ptr<Widget> spw) // call these only when
{
return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
return 0.0;
}
bool f3(Widget* pw) // mutex is locked
{
return false;
}
using MuxGuard = std::lock_guard<std::mutex>;
template<typename FuncType,
typename MuxType,
typename PtrType>
auto
lockAndCall(FuncType func,
MuxType& mutex, // mutex 以引用傳遞
PtrType ptr) -> decltype(func(ptr))
{
MuxGuard g(mutex);
return func(ptr);
}
int main()
{
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
auto result1 = lockAndCall(f1, f1m, 0); // error!
auto result2 = lockAndCall(f2, f2m, NULL); // error!
auto result3 = lockAndCall(f3, f3m, nullptr); // fine
}
#include <mutex>
#include <memory>
class Widget
{
};
int f1(std::shared_ptr<Widget> spw) // call these only when
{
return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
return 0.0;
}
bool f3(Widget* pw) // mutex is locked
{
return false;
}
using MuxGuard = std::lock_guard<std::mutex>;
template<typename FuncType,
typename MuxType,
typename PtrType>
auto
lockAndCall(FuncType func,
MuxType& mutex, // mutex 以引用傳遞
PtrType ptr) -> decltype(func(ptr))
{
MuxGuard g(mutex);
return func(ptr);
}
int main()
{
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
auto result1 = lockAndCall(f1, f1m, nullptr); // fine
auto result2 = lockAndCall(f2, f2m, nullptr); // fine
auto result3 = lockAndCall(f3, f3m, nullptr); // fine
}
item15 只要可能就用 constexpr
: 編譯期計算 / 運行期計算(不能進(jìn)行編譯期計算時, 自動降級為 運行期計算
)
1 用于對象
constexpr auto arraySize2 = 10; // fine, 10 is a
std::array<int, arraySize2> data2; // fine, arraySize2
// is constexpr
2 用于非成員函數(shù)
constexpr // pow's a constexpr func
int pow(int base, int exp) noexcept // that never throws, base^exp
{
return (exp == 0 ? 1 : base * pow(base, exp - 1) ); // C++11: 只支持函數(shù)體含1行語句, 可以是遞歸
}
constexpr int
pow(int base, int exp) noexcept // C++14: 支持函數(shù)體含多行語句
{
auto result = 1;
for (int i = 0; i < exp; ++i)
result *= base;
return result;
}
(1) 編譯期值作實參 調(diào)用
, 返回 編譯期結(jié)果
constexpr auto numConds = 5; // # of conditions
std::array<int, pow(3, numConds)> results; // results has
// 3^numConds
// elements
(2) 運行期值作實參 調(diào)用
, 返回 運行期結(jié)果
auto base = readFromDB("base"); // get these values
auto exp = readFromDB("exponent"); // at runtime
auto baseToExp = pow(base, exp); // call pow function
// at runtime
3 用于成員函數(shù): 運行期構(gòu)造
+ 調(diào)成員函數(shù)
class Point {
public:
constexpr Point(double xVal = 0, double yVal = 0) noexcept
: x(xVal), y(yVal) {}
constexpr double xValue() const noexcept { return x; }
constexpr double yValue() const noexcept { return y; }
void setX(double newX) noexcept { x = newX; }
void setY(double newY) noexcept { y = newY; }
private:
double x, y;
};
constexpr Point p1(9.4, 27.7); // fine, "runs" constexpr
// ctor during compilation
constexpr Point p2(28.8, 5.3); // also fine
constexpr
Point midpoint(const Point& p1, const Point& p2) noexcept
{
return { (p1.xValue() + p2.xValue()) / 2,
(p1.yValue() + p2.yValue()) / 2 }; // call constexpr member funcs
}
constexpr auto mid = midpoint(p1, p2); // init constexpr
// object w/result of
// constexpr function
item16 使 const 成員函數(shù) 線程安全
原因: const 成員函數(shù)
中修改 mutable 成員
(如 cache + cacheIsValid) + 多線程
-> not 線程安全
remember
(1) 確保 const 成員函數(shù)線程安全, 除非你確定不會在并發(fā) context 中使用
(2) std::atomic
變量可能比 mutex 性能高, 但只適用于
操作單個 變量或內(nèi)存位置
class Polynomial
{
public:
using RootsType = std::vector<double>;
RootsType
roots() const
{
std::lock_guard<std::mutex> g(m); // lock mutex
if (!rootsAreValid) // if cache not valid
{
… // compute,
// store them in rootVals
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::mutex m;
mutable bool rootsAreValid{ false }; // see Item 7 for initializers
mutable RootsType rootVals{};
};