Effective Modern C++ - 3: 步入 Modern C++

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{};       
};
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 前言 Google Play应用市场对于应用的targetSdkVersion有了更为严格的要求。从 2018 年...
    申国骏阅读 64,088评论 14 98
  • 《来,我们说说孤独》 1·他们都在写孤独 一个诗人 如果 不说说 内心的孤独 不将孤独 写进诗里 是不是很掉价呢 ...
    听太阳升起阅读 4,376评论 1 7
  • 自幼贫民窟长大的女子,侥幸多念了两本书,枉以为可以与人平起平坐。可是人生从来都是接力赛,我们却天真的当成了百米冲刺...
    Leeanran阅读 5,770评论 1 5
  • 云舒老师,姓甚名谁,男的女的,多大岁数,这些我全然不知。之所以要写写云舒老师,完全是因为他写的文章,如一个巨大的磁...
    数豆者m阅读 2,351评论 6 9
  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 2,890评论 0 5