Effective Modern C++

【Effective Modern C++(3)】转向现代C++

2018-11-30  本文已影响31人  downdemo

07 创建对象时注意区分()和{}

int x(0); // 初始化值在小括号中
int y = 0; // 初始化值在等号后
int z{ 0 }; // 初始化值在大括号中
int z = { 0 };
Widget w1; // 默认构造函数
Widget w2 = w1; // 拷贝而非赋值
w1 = w2; // 拷贝而非赋值
std::vector<int> v{ 1, 3, 5 }; // v的初始内容为1、3、5
class Widget {
    …
private:
    int x{ 0 }; // x的默认值是0
    int y = 0; // y的默认值是0
    int z(0); // 错误
};
std::atomic<int> ai1{ 0 }; // OK
std::atomic<int> ai2(0); // OK
std::atomic<int> ai3 = 0; // 错误
double x, y, z;
…
int sum1{ x + y + z }; // 错误:double和不允许表达为int
int sum2(x + y + z); // OK:表达式的值被截断为int
int sum3 = x + y + z; // 同上
Widget w1(10); // 调用构造函数
Widget w2(); // most vexing parse,声明一个函数
Widget w3{}; // 调用构造函数,而没有上面的问题
class Widget {
public:
    Widget(int i, bool b); // 没有std::initializer_list类型参数
    Widget(int i, double d); // 没有std::initializer_list类型参数
    …
};

Widget w1(10, true); // 调用第一个构造函数
Widget w2{10, true}; // 同上
Widget w3(10, 5.0); // 调用第二个构造函数
Widget w4{10, 5.0}; // 同上
class Widget {
public:
    Widget(int i, bool b); // 没有std::initializer_list类型参数
    Widget(int i, double d); // 没有std::initializer_list类型参数
    Widget(std::initializer_list<long double> il); // 新增的构造函数
    …
};

Widget w1(10, true); // 调用第一个构造函数
Widget w2{10, true}; // 调用第三个构造函数,10和true转为long double
Widget w3(10, 5.0); // 调用第二个构造函数
Widget w4{10, 5.0}; // 调用第三个构造函数,10和5.0转为long double
class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<long double> il);
    operator float() const; // 强制转为float
    …
};

Widget w5(w4); // 拷贝构造
Widget w6{w4}; // std::initializer_list版本拷贝构造(w4转为float,float再转为long double)
Widget w7(std::move(w4)); // 移动构造
Widget w8{std::move(w4)}; // std::initializer_list版本移动构造(处理同w4)
class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<bool> il);
    …
}; 

Widget w{10, 5.0}; // 错误:需要窄化转换为bool
class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<std::string> il);
    …
}; 

Widget w1{10, true}; // int和bool无法转为std::string,转而调用第一个构造函数
Widget w2{10, 5.0}; // int和bool无法转为std::string,转而调用第二个构造函数
class Widget {
public:
    Widget();
    Widget(std::initializer_list<int> il);
};
Widget w1; // 调用默认构造函数
Widget w2{}; // 同上
Widget w3(); // most vexing parse,声明一个函数
Widget w4({}); // 调用std::initializer_list版本构造函数
Widget w5{{}}; // 同上
std::vector<int> v1(10, 20); // 使用non-std::initializer_list构造函数,初始化内容为10个20
std::vector<int> v2{10, 20}; // 使用std::initializer_list版本构造函数,初始化内容为10和20
template<typename T, typename... Ts>
void doSomeWork(Ts&&... params)
{
    用params创建T类型局部对象 // 下面有两种实现方式
    …
}
T localObject(std::forward<Ts>(params)...); // 使用小括号
T localObject{std::forward<Ts>(params)...}; // 使用大括号
std::vector<int> v;
…
doSomeWork<std::vector<int>>(10, 20);

08 使用nullptr替代0和NULL

void f(bool) { cout << "1"; }
void f(int) { cout << "2"; }
void f(void*) { cout << "3"; }

int main()
{
    f(0); // 2
    f(NULL); // 2,也可能不通过编译
    f(nullptr); // 3
}
auto result = findRecord( /* arguments */ );
if (result == 0) { ... }
// 仅当mutex加锁时才调用这些函数
int f1(std::shared_ptr<Widget> spw);
double f2(std::unique_ptr<Widget> upw);
bool f3(Widget* pw);
std::mutex f1m, f2m, f3m;
using MuxGuard = std::lock_guard<std::mutex>;
…
{
    MuxGuard g(f1m); // lock mutex for f1
    auto result = f1(0);
} // unlock mutex
…
{
    MuxGuard g(f2m); // lock mutex for f2
    auto result = f2(NULL);
} // unlock mutex
…
{
    MuxGuard g(f3m); // lock mutex for f3
    auto result = f3(nullptr);
} // unlock mutex
template<typename FuncType,
    typename MuxType,
    typename PtrType>
auto lockAndCall(FuncType func,
    MuxType& mutex,
    PtrType ptr) -> decltype(func(ptr)) // C++14可以简化为decltype(auto)
{
    MuxGuard g(mutex);
    return func(ptr);
}
auto result1 = lockAndCall(f1, f1m, 0); // 错误
auto result2 = lockAndCall(f2, f2m, NULL); // 错误
auto result3 = lockAndCall(f3, f3m, nullptr); // OK

09 使用别名声明替代typedef

typedef void (*FP)(int, const std::string&);
using FP = void (*)(int, const std::string&);
template<typename T> // MyAllocList<T>是std::list<T, MyAlloc<T>>的同义词
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw;

// 如果要用typedef
template<typename T> // MyAllocList<T>::type是std::list<T, MyAlloc<T>>的同义词
struct MyAllocList {
    typedef std::list<T, MyAlloc<T>> type;
};
MyAllocList<Widget>::type lw;
template<typename T>
class Widget { // Widget<T>包含一个MyAllocList<T>类型数据成员
private:
    typename MyAllocList<T>::type list;
    …
};
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;

template<typename T>
class Widget {
private:
    MyAllocList<T> list;
    …
};
std::remove_const<T>::type // 由const T生成T
std::remove_reference<T>::type // 由T&或T&&生成T
std::add_lvalue_reference<T>::type // 由T生成T&
std::remove_const<T>::type // C++11: const T → T
std::remove_const_t<T> // C++14 equivalent
std::remove_reference<T>::type // C++11: T&/T&& → T
std::remove_reference_t<T> // C++14 equivalent
std::add_lvalue_reference<T>::type // C++11: T → T&
std::add_lvalue_reference_t<T> // C++14 equivalent
template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <class T>
using add_lvalue_reference_t =
typename add_lvalue_reference<T>::type;

10 使用限定作用域enum替代非限定作用域enum

enum Color { black, white, red };
auto white = false; // 错误:white已在作用域内声明过
enum class Color { black, white, red };
auto white = false; // OK
Color c = white; // 错误:作用域中没有名为white的枚举量
Color c = Color::white; // OK
auto c = Color::white; // OK
enum Color { black, white, red }; // unscoped enum

std::vector<std::size_t> primeFactors(std::size_t x); // 返回x的质因数的函数

Color c = red;
…
if (c < 14.5) { // 将Color类型和double比较
    auto factors = primeFactors(c);
    …
}
enum class Color { black, white, red };

std::vector<std::size_t> primeFactors(std::size_t x); // 返回x的质因数的函数

Color c = Color::red; // 现在要指定范围修饰词
…
if (c < 14.5) { // 错误:不能将Color类型和double比较
    auto factors = primeFactors(c); // 错误:不能将Color类型转为std::size_t
    …
}
if (static_cast<double>(c) < 14.5) {
    auto factors = primeFactors(static_cast<std::size_t>(c)); // 可能不合法
    …
}
enum Color; // C++11之前错误
enum class Color; // OK
enum Color { black, white, red };
enum Status {
    good = 0,
    failed = 1,
    incomplete = 100,
    corrupt = 200,
    indeterminate = 0xFFFFFFFF
};
enum Status {
    good = 0,
    failed = 1,
    incomplete = 100,
    corrupt = 200,
    audited = 500, // 添加一个成员
    indeterminate = 0xFFFFFFFF
};
enum class C;
void f(C c);
enum class Status: std::uint32_t; // 指定类型为<cstdint>中的std::unit32_t
enum Color: std::uint8_t; // 指定unscoped enum的底层类型为std::unit8_t
// 也可以在定义中指定
enum class Status: std::uint32_t {
    good = 0,
    failed = 1,
    incomplete = 100,
    corrupt = 200,
    audited = 500,
    indeterminate = 0xFFFFFFFF
};
// tuple表示用户信息,元素分别用来表示名字、邮箱、声望值
using UserInfo = std::tuple<std::string, std::string, std::size_t>;
UserInfo uInfo;
…
auto val = std::get<1>(uInfo); // 获取第1个值(从0开始)
enum UserInfoFields { uiName, uiEmail, uiReputation };
using UserInfo = std::tuple<std::string, std::string, std::size_t>;
UserInfo uInfo;
…
auto val = std::get<uiEmail>(uInfo);
enum class UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // as before
…
auto val = std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>(uInfo);
template<typename E>
constexpr typename std::underlying_type<E>::type
toUType(E enumerator) noexcept
{
    return static_cast<typename std::underlying_type<E>::type>(enumerator);
}
template<typename E>
constexpr std::underlying_type_t<E>
toUType(E enumerator) noexcept
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}
template<typename E>
constexpr auto
toUType(E enumerator) noexcept
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}
auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);

11 使用=delete替代private未定义函数

template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
    …
private:
    basic_ios(const basic_ios& ); // not defined
    basic_ios& operator=(const basic_ios&); // not defined
};
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
    …
    basic_ios(const basic_ios& ) = delete;
    basic_ios& operator=(const basic_ios&) = delete;
    …
};
void f(int);
void f(double) = delete; // 拒绝double和float类型参数
void f(char) = delete;
void f(bool) = delete;
f(3.14); // 错误
f('a'); // 错误
f(true); // 错误
template<typename T>
void processPointer(T* ptr);
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
template<>
void processPointer<const void>(const void*) = delete;
template<>
void processPointer<const char>(const char*) = delete;
class Widget {
public:
    …
    template<typename T>
    void processPointer(T* ptr)
    { … }
private:
    template<> // 错误
    void processPointer<void>(void*);
};

class Widget {
public:
    …
    template<typename T>
    void processPointer(T* ptr)
    { … }
    …
};

template<> // 仍然是public但被删除了
void Widget::processPointer<void>(void*) = delete;

12 将要重写的函数声明为override

class Base {
public:
    virtual void doWork(); // base class virtual function
    …
};
class Derived: public Base {
public:
    virtual void doWork(); // 重写Base::doWork
    …
};

std::unique_ptr<Base> upb = std::make_unique<Derived>();
…
upb->doWork(); // 通过基类指针调用派生类的doWork
class Widget {
public:
    …
    void doWork() &; // *this是左值时才使用
    void doWork() &&; // *this是右值时才使用
};
…
Widget makeWidget(); // 工厂函数(返回右值)
Widget w; // 常规左值对象
…
w.doWork(); // 调用Widget::doWork &
makeWidget().doWork(); // 调用Widget::doWork &&
class Base {
public:
    virtual void mf1() const;
    virtual void mf2(int x);
    virtual void mf3() &;
    void mf4() const;
};
class Derived: public Base {
public:
    virtual void mf1();
    virtual void mf2(unsigned int x);
    virtual void mf3() &&;
    void mf4() const;
};
class Base {
public:
    virtual void mf1() const;
    virtual void mf2(int x);
    virtual void mf3() &;
    virtual void mf4() const;
};
class Derived: public Base {
public:
    virtual void mf1() const override;
    virtual void mf2(int x) override;
    virtual void mf3() & override;
    void mf4() const override;
};
class Warning {
public:
    …
    void override(); // 在C++98和C++11中都合法
    …
};

成员函数引用修饰符

class A {
public:
    using DataType = std::vector<double>;
    …
    DataType& data() { return values; }
    …
private:
    DataType values;
};

A a;
…
auto val1 = a.data(); // 返回std::vector<double>&
Widget makeA();
auto val2 = makeA().data(); // 仍是返回左值引用
class A {
public:
    using DataType = std::vector<double>;
    …
    DataType& data() & { return values; }
    DataType data() && { return std::move(values); } // 注意返回类型不是引用了
    …
private:
    DataType values;
};

auto val1 = a.data(); // 调用左值重载版本,拷贝构造val1
auto val2 = makeA().data(); // 调用右值重载版本,移动构造val2

13 使用const_iterator替代iterator

std::vector<int> values;
…
std::vector<int>::iterator it = std::find(values.begin(),values.end(), 1983);
values.insert(it, 1998);
typedef std::vector<int>::iterator IterT;
std::vector<int>::const_iterator ConstIterT;
std::vector<int> values;
…
ConstIterT ci = // const容器才能得到const_iterator,非const容器则需要强制转换
    std::find(static_cast<ConstIterT>(values.begin()), static_cast<ConstIterT>(values.end()),  1983);
values.insert(static_cast<IterT>(ci), 1998); // C++98中插入位置只能用iterator指定,但这不能通过编译
std::vector<int> values;
…
auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);
// C++14
template<typename C, typename V>
void findAndInsert(C& container, const V& targetVal, const V& insertVal)
{
    using std::cbegin;
    using std::cend;
    auto it = std::find(cbegin(container), cend(container), targetVal);
    container.insert(it, insertVal);
}
template <class C> // C可以是内置数组类型,begin为数组提供了一个特化版本
auto cbegin(const C& container)->decltype(std::begin(container))
{
    return std::begin(container); // container是const,所以返回const_iterator
}

14 将不抛异常的函数声明为noexcept

int f(int x) throw(); // C++98
int f(int x) noexcept; // C++11
RetType function(params) noexcept; // most optimizable
RetType function(params) throw(); // less optimizable
RetType function(params); // less optimizable
std::vector<Widget> vw;
…
Widget w;
… // work with w
vw.push_back(w); // add w to vw
template <class T, size_t N>
void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b))); // 见后续解释
template <class T1, class T2>
struct pair {
    …
    void swap(pair& p) noexcept(noexcept(swap(first, p.first)) &&
        noexcept(swap(second, p.second)));
    …
};
void f(const std::string& s) noexcept; // 前置条件:s.length() <= 32
void setup(); // functions defined elsewhere
void cleanup();
void doWork() noexcept
{
    setup(); // set up work to be done
    … // do the actual work
    cleanup(); // perform cleanup actions
}

15 尽可能使用constexpr

int i; // non-constexpr变量
…
constexpr auto j = i; // 错误:i的值在编译期未知
std::array<int, i> v1; // 错误:同上
constexpr auto n = 10; // OK:10是一个编译期常量
std::array<int, n> v2; // OK:n是constexpr
int i; // non-constexpr变量
…
const auto n = i; // OK
std::array<int, n> v; // 错误:n值非编译期已知
constexpr int pow(int base, int exp) noexcept
{
    … // 实现见后
}
constexpr auto n = 5;
std::array<int, pow(3, n)> results;
auto base = readFromDB("base"); // 运行期获取值
auto exp = readFromDB("exponent"); // 运行期获取值
auto baseToExp = pow(base, exp); // pow在运行期被调用
constexpr int pow(int base, int exp) noexcept
{
    return (exp == 0 ? 1 : base * pow(base, exp - 1));
}
// C++14
constexpr int pow(int base, int exp) noexcept
{
    auto result = 1;
    for (int i = 0; i < exp; ++i) result *= base;
    return result;
}
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; } // 修改了对象所以不能声明为constexpr
    void setY(double newY) noexcept { y = newY; } // 另一个原因是返回的void不是字面值类型
private:
    double x, y;
};

constexpr Point p1(9.4, 27.7); // 编译期执行constexpr构造函数
constexpr Point p2(28.8, 5.3); // 同上

// 通过constexpr Point对象调用xValue和yValue也会在编译期获取值
// 于是可以再写出一个新的constexpr函数
constexpr
Point midpoint(const Point& p1, const Point& p2) noexcept
{
    return { (p1.xValue() + p2.xValue()) / 2, (p1.yValue() + p2.yValue()) / 2 };
}
constexpr auto mid = midpoint(p1, p2); // mid在编译期创建
mid.xValue()*10
// 因为上式是浮点型,浮点型不能用于模板实例化,因此还要如下转换一次
static_cast<int>(mid.xValue()*10)
// C++14
class Point {
public:
    …
    constexpr void setX(double newX) noexcept { x = newX; }
    constexpr void setY(double newY) noexcept { y = newY; }
    …
};
// 于是C++14允许写出下面的代码
constexpr Point reflection(const Point& p) noexcept // 返回p关于原点的对称点
{
    Point result; // 创建non-const Point
    result.setX(-p.xValue()); // setX
    result.setY(-p.yValue()); // setY
    return result;
}

constexpr Point p1(9.4, 27.7);
constexpr Point p2(28.8, 5.3);
constexpr auto mid = midpoint(p1, p2);
constexpr auto reflectedMid = reflection(mid); // 值为(-19.1, -16.5),且在编译期已知

16 保证const成员函数线程安全

class Polynomial {
public:
    using RootsType = std::vector<double>; // 存储根的数据结构
    RootsType roots() const
    {
        if (!rootsAreValid) { // 缓存无效则计算根
            …
            rootsAreValid = true;
        }
        return rootVals;
    }
private:
    mutable bool rootsAreValid{ false };
    mutable RootsType rootVals{};
};
Polynomial p;
…
/*----- Thread 1 ----- */ /*------- Thread 2 ------- */
auto rootsOfP = p.roots(); auto valsGivingZero = p.roots();
public:
    using RootsType = std::vector<double>; // 存储根的数据结构
    RootsType roots() const
    {
        std::lock_guard<std::mutex> g(m); // lock mutex
        if (!rootsAreValid) { // 缓存无效则计算根
            …
            rootsAreValid = true;
        }
        return rootVals;
    } // unlock mutex
private:
    mutable std::mutex m;
    mutable bool rootsAreValid{ false };
    mutable RootsType rootVals{};
};
class Point {
public:
    …
    double distanceFromOrigin() const noexcept
    {
        ++callCount; // 原子性的自增操作
        return std::sqrt((x * x) + (y * y));
    }
private:
    mutable std::atomic<unsigned> callCount{ 0 };
    double x, y;
};
class Widget {
public:
    …
    int magicValue() const
    {
        if (cacheValid) return cachedValue;
        else {
            auto val1 = expensiveComputation1();
            auto val2 = expensiveComputation2();
            cachedValue = val1 + val2; // part1
            cacheValid = true; // part2
            return cachedValue;
        }
    }
private:
    mutable std::atomic<bool> cacheValid{ false };
    mutable std::atomic<int> cachedValue;
};
class Widget {
public:
    …
    int magicValue() const
    {
        if (cacheValid) return cachedValue;
        else {
            auto val1 = expensiveComputation1();
            auto val2 = expensiveComputation2();
            cacheValid = true; // part1
            cachedValue = val1 + val2; // part2
            return cachedValue;
        }
    }
private:
    mutable std::atomic<bool> cacheValid{ false };
    mutable std::atomic<int> cachedValue;
};
class Widget {
public:
    …
    int magicValue() const
    {
        std::lock_guard<std::mutex> guard(m); // lock m
        if (cacheValid) return cachedValue;
        else {
            auto val1 = expensiveComputation1();
            auto val2 = expensiveComputation2();
            cachedValue = val1 + val2;
            cacheValid = true;
            return cachedValue;
        }
    } // unlock m
    …
private:
    mutable std::mutex m;
    mutable int cachedValue; // 不再有原子性
    mutable bool cacheValid{ false }; // 不再有原子性
};

17 理解特殊成员函数的生成机制

class A {
public:
    …
    A(A&& rhs); // 移动构造函数
    A& operator=(A&& rhs); // 移动赋值运算符
    …
};
class A {
public:
    …
    ~A(); // 用户定义的析构函数
    …
    A(const A&) = default; // 显式声明使用默认拷贝构造函数的行为
    A& operator=(const A&) = default; // 显式声明默认拷贝赋值运算符
    …
};
class A {
public:
    virtual ~A() = default;
    A(A&&) = default; // support moving
    A& operator=(A&&) = default;
    A(const A&) = default; // support copying
    A& operator=(const A&) = default;
    …
};
class StringTable { // 表示字符串表格的类
public:
    StringTable() {}
    … // 实现插入、删除、查找等函数
private:
    std::map<int, std::string> values;
};
class StringTable {
public:
    StringTable() { makeLogEntry("Creating StringTable object"); }
    ~StringTable() { makeLogEntry("Destroying StringTable object"); }
    …
private:
    std::map<int, std::string> values; // as before
};
class A {
…
    template<typename T>
    A(const T& rhs); // 从任意类型构造
    template<typename T>
    A& operator=(const T& rhs); // 从任意类型赋值
    …
};
上一篇 下一篇

猜你喜欢

热点阅读