item8 nullptr
优先于 0 和 NULL
总结:
(1) 0: 0 是 int 型
, 编译器将 应该出现指针却出现0
的位置上的0 勉强转换为 空指针 (void*)0
(2) NULL: C++ 中可能被编译器底层实现为 整型 (int/long)
Note: C
中, 还可能被编译器底层实现为空指针 (void*)0
(3) nullptr: 可 隐式转换
为所有/任意类型
的裸指针类型(进而可以被 智能指针形参 接收: 构造出 智能指针)
, 不会被视为
任何整型
nullptr 的类型是 std::nullptr_t
std::nullptr_t 可隐式转换为 all pointer types
分析: 相对于 0/NULL,
1 nullptr 可避免 重载解析
时的混淆/错误
0/NULL 作实参
, 调重载函数 f(int/bool/void*)
时, 必然调用 f(int), 而非 f(void*) / 行为不确定
原因: 若 NULL 实现为 0L, 0L(long) -> int/bool/void*
被编译器认为同样好
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
结合时
(1) 用 0 看不出 函数的返回类型
auto result = findRecord( /* arguments */ );
if (result == 0)
{
…
}
(2) 用 0 能看出函数的返回类型
必然为指针类型
auto result = findRecord( /* arguments */ );
if (result == nullptr)
{
…
}
3 nullptr 在模板
中优势最大
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 再试图传给 智能指针型形参的函数 f1/f2
时, 类型错误
, 编译报错
nullptr 的类型是 std::nullptr_t => PtrType 为 std::nullptr_t, 再传给 f3时, std::nullptr_t 隐式转换为 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 调用
例1: 0/NULL 能直接传给 智能指针: 先隐式转换为空指针, 再构造出智能指针
#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次模板实参推断后
, 再通过隐式转换
为空指针而间接传给智能指针
原因: 函数模板实例化
过程中, 函数模板实参推断后
, 只能按推断出的类型传递给另一函数, 不再进行隐式转换
#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
: 编译期计算 / 运行期计算(不能进行编译期计算时, 自动降级为 运行期计算
)
1 用于对象
constexpr auto arraySize2 = 10; // fine, 10 is a
std::array<int, arraySize2> data2; // fine, arraySize2
// is constexpr
2 用于非成员函数
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: 只支持函数体含1行语句, 可以是递归
}
constexpr int
pow(int base, int exp) noexcept // C++14: 支持函数体含多行语句
{
auto result = 1;
for (int i = 0; i < exp; ++i)
result *= base;
return result;
}
(1) 编译期值作实参 调用
, 返回 编译期结果
constexpr auto numConds = 5; // # of conditions
std::array<int, pow(3, numConds)> results; // results has
// 3^numConds
// elements
(2) 运行期值作实参 调用
, 返回 运行期结果
auto base = readFromDB("base"); // get these values
auto exp = readFromDB("exponent"); // at runtime
auto baseToExp = pow(base, exp); // call pow function
// at runtime
3 用于成员函数: 运行期构造
+ 调成员函数
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 成员函数 线程安全
原因: const 成员函数
中修改 mutable 成员
(如 cache + cacheIsValid) + 多线程
-> not 线程安全
remember
(1) 确保 const 成员函数线程安全, 除非你确定不会在并发 context 中使用
(2) std::atomic
变量可能比 mutex 性能高, 但只适用于
操作单个 变量或内存位置
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{};
};
网友评论