本文介绍: 使用RAII(“Resource Acquisition Is Initialization资源获得即初始化包装thread,在析构时进行join。和闭包存在编译期,闭包存在运行时。

Effective Modern C++(C++14)

GitHub – CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C++》- 完成翻译

Deducing Types

  1. 模版类型推导
    1. 引用constvolatile忽略
    2. 数组名函数退化指针
    3. 通用引用:
      1. 左值保持引用和const
      2. 右值忽略引用和const
  2. auto 类型推导
    1. {} 初始化auto推导std::initializer_list
    2. 模版推导无法解析 {} 初始化
    3. 不可见的代理可能会使auto表达式推导出“错误的”类型
  3. decltype 类型推导
    1. decltype用于变量名会产生该变量名声明类型
    2. 返回decltype-》auto + decltype返回类型
    3. autodecltype(auto)不同decltype保留了auto忽略的左值引用
decltype(x+y) add(T x, U y){}; // 编译错误

template <typename T, typename U>
auto add(T x, U y) -> decltype(x+y) 
{ 
    return x+y; 
} 

template <typename T, typename U&gt; // C++ 14
auto add(T x, U y) 
{ 
    return x+y; 
}

decltype(auto) f1()
{
    int x = 0;
    return x;                            //decltype(x)是int,所以f1返回int
}

decltype(auto) f2()
{
    int x = 0;
    return (x);                          //decltype((x))是int&amp;,所以f2返回int&amp;
}

  1. 查看类型推导结果
    1. 编译期:编译期报错诊断
    2. 运行时:
      1. typeid依赖编译期解析,可能不正确
      2. Boost.TypeIndex
template<typename T&gt;                //只对TD进行声明
class TD;                           //TD == "Type Displayer"
// 如果尝试实例这个模板就会引出一个错误消息,因为这里没有用来实例化的类模板定义。为了查看x和y的类型,只需要使用它们的类型去实例化TD:
TD<decltype(x)&gt; xType;              //引出包含x和y
TD<decltype(y)&gt; yType;              //的类型的错误消息


template<typename T>
void f(const T&amp; param)
{
    using std::cout;
    cout << "T =     " << typeid(T).name() << 'n';             //显示T
    cout << "param = " << typeid(param).name() << 'n';         //显示
}                                                               //的类型


#include <boost/type_index.hpp>

template<typename T>
void f(const T&amp; param)
{
    using std::cout;
    using boost::typeindex::type_id_with_cvr;
    //显示T
    cout << "T =     "
         << type_id_with_cvr<T>().pretty_name()
         << 'n';    
    //显示param类型
    cout << "param = "
         << type_id_with_cvr<decltype(param)>().pretty_name()
         << 'n';
}

Modern C++:98 vs 11

  1. 构造拷贝构造,拷贝复制
Widget w1;              //调用默认构造函数
Widget w2 = w1;         //不是赋值运算调用拷贝构造函数
w1 = w2;                //是赋值运算调用拷贝赋值运算符copy operator=)
  1. 初始化:(),=,{}
    1. ():构造,无参构造和函数声明语义冲突,无参构造应该省略()
    2. =:拷贝构造,拷贝赋值
    3. {}:统一初始化,不允许类型变窄
  2. std::initializer_list 和 {} 初始化永远是最佳匹配
  3. () 初始化和 {} 初始化可能会有巨大不同:ex:std::vector
  4. 重载指针参数整型参数
    1. 0:int
    2. NULL:int or long
    3. nullptrstd::nullptr_t,可以隐式转换指向任何内置类型的指针
  5. **typedef(C++98)和using(C++11):**using别名模版简单,不需要::type和typename
template<typename T>                            //MyAllocList<T>是
using MyAllocList = std::list<T, MyAlloc<T>>;   //std::list<T, MyAlloc<T>>
MyAllocList<Widget> lw;                         //用户代码

template<typename T>                            //MyAllocList<T>是
struct MyAllocList {                            //std::list<T, MyAlloc<T>>
    typedef std::list<T, MyAlloc<T>> type;      //的同义词  
};
MyAllocList<Widget>::type lw;                   //用户代码

template<typename T>
class Widget {                              //Widget<T>含有一个
private:                                    //MyAllocLIst<T>对象
    typename MyAllocList<T>::type list;     //作为数据成员
}; 

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; 
  1. 非限域Enum(C+98)和限域Enum(C++11):限域class enum不污染命名空间,都支持底层类型,但是Enum class不能隐式转换只能cast
enum Color { black, white, red };   //black, white, red在Color所在的作用域
auto white = false;                 //错误! white早已在这个作用域声明


enum class Color { black, white, red }; //black, white, red限制在Color域内
auto white = false;                     //没问题,域内没有其他“white”
Color c = white;                        //错误,域中没有枚举名叫white
Color c = Color::white;                 //没问题

using UserInfo =                //类型别名,参见Item9
    std::tuple<std::string,     //名字
               std::string,     //email地址
               std::size_t> ;   //声望

enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo;                         //同之前一样
auto val = std::get<uiEmail>(uInfo);    //啊,获取用户email字段的值

template<typename E>                //C++14
constexpr std::underlying_type_t<E>
    toUType(E enumerator) noexcept
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}
auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);
  1. 定义私有声明(C++98)和deleted(C++11):
    1. 删除自动生成函数:都可
    2. 删除隐式转换类型的函数重载:only deleted
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
private:
    basic_ios(const basic_ios&amp; );           // not defined
    basic_ios&amp; operator=(const basic_ios&amp;); // not defined
};


template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
    basic_ios(const basic_ios&amp; ) = delete;
    basic_ios&amp; operator=(const basic_ios&amp;) = delete;
};


bool isLucky(int number);       //原始版本
bool isLucky(char) = delete;    //拒绝char
bool isLucky(bool) = delete;    //拒绝bool
bool isLucky(double) = delete;  //拒绝floatdouble

template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
class Widget {
public:
    template<typename T>
    void processPointer(T* ptr)
};
template<>                                          //还是public
void Widget::processPointer<void>(void*) = delete;  //但是已经被删除

  1. 显式overridefinal实现重载和避免重载:C++11新增引用限定
class Widget {
public:
    using DataType = std::vector<double>;
    DataType& data() &              //对于左值Widgets,
    { return values; }              //返回左值
    DataType data() &&              //对于右值Widgets,
    { return std::move(values); }   //返回右值
private:
    DataType values;
};

  1. 优先使用const_iterator而非iterator
  2. **beginend,cbegin,rbegin的非成员函数通用性更强,**可用于原生数组
  3. **尽可能使用noexcept:**可以编译优化noexcept函数可以调用异常中立函数
RetType function(params) noexcept;  //极尽所能优化	//C++11风格,没有来自f的异常
RetType function(params) throw();   //较少优化		//C++98风格,没有来自f的异常
RetType function(params);           //较少优化

  1. **const 常量,编译期不一定可知;constexpr 编译期常量,编译期可知;constexpr 函数值根据参数在编译期是否可知决定,**constexpr函数既可以是运行时也可以是编译期
  2. const成员函数中的mutable成员变量,需要mutexatomic来保证线程安全
  3. 特殊成员函数:构造,析构,拷贝构造,拷贝赋值,移动构造,移动赋值
  4. **Role of Three:**自定义资源管理时,析构,拷贝构造,拷贝赋值同时自定义
  5. 移动操作仅当类没有显式声明移动操作,拷贝操作,析构函数时才自动生成
  6. 自定义移动时,相应拷贝被deleted,在自定义析构时,拷贝不自动生成
  7. **置入emplace_back优于插入push_back:**避免了临时对象创建销毁

Smart Pointers

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

// 原 header
class Widget() {                    //定义头文件widget.h”
public:
    Widget();private:
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;              //Gadget是用户自定义的类型
};

// 新 header
class Widget {                          //仍然在“widget.h”中
public:
    Widget();
    ~Widget();

    Widget(Widget&& rhs);               //只有声明
    Widget& operator=(Widget&& rhs);private:                                //跟之前一样
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// 新 cpp
#include <string>                   //跟之前一样,仍然在“widget.cpp”中struct Widget::Impl {};          //跟之前一样

Widget::Widget()                    //跟之前一样
: pImpl(std::make_unique<Impl>())
{}

Widget::~Widget() = default;        //跟之前一样

Widget::Widget(Widget&& rhs) = default;             //这里定义
Widget& Widget::operator=(Widget&& rhs) = default;

Rvalue, Move & Forward

template<typename T>
void logAndAdd(T&& name)
{
    logAndAddImpl(
        std::forward<T>(name),
        std::is_integral<typename std::remove_reference<T>::type>()
    );
}

template<typename T>                            //非整型实参:添加全局数据结构
void logAndAddImpl(T&& name, std::false_type)	//译者注:高亮std::false_type
{
    auto now = std::chrono::system_clock::now();
    log(now, "logAndAdd");
    names.emplace(std::forward<T>(name));
}

std::string nameFromIdx(int idx);           //与条款26一样,整型实参:查找名字并用它调用logAndAdd
void logAndAddImpl(int idx, std::true_type) //译者注:高亮std::true_type
{
  logAndAdd(nameFromIdx(idx)); 
}
class Person  {                                         //C++14
public:
    template<
        typename T,
        typename = std::enable_if_t<                    //这儿更少的代码
                       !std::is_base_of<Person,
                                        std::decay_t<T> //还有这儿
                                       >::value
                   >                                    //还有这儿
    >
    explicit Person(T&& n);};
template<typename T>
void fwd(T&& param)             //接受任何实参
{
    f(std::forward<T>(param));  //转发给f
}

template<typename... Ts>
void fwd(Ts&&... params)            //接受任何实参
{
    f(std::forward<Ts>(params)...); //转发给f
}
  • 重载拷贝左值,移动右值-》完美转发-》直接传值移动:
    • 一次额外的移动,适用移动开销低
      • 左值:拷贝+移动
      • 右值:移动+移动
    • 实现简单,不用重载,不用模版
class Widget {                                  //方法1:对左值和右值重载
public:
    void addName(const std::string& newName)
    { names.push_back(newName); } // rvalues
    void addName(std::string&& newName)
    { names.push_back(std::move(newName)); }
private:
    std::vector<std::string> names;
};

class Widget {                                  //方法2:使用通用引用
public:
    template<typename T>
    void addName(T&& newName)
    { names.push_back(std::forward<T>(newName)); }
};

class Widget {                                  //方法3:传值
public:
    void addName(std::string newName)
    { names.push_back(std::move(newName)); }
};

Lambda

lambdas闭包类存在于编译期,闭包存在于运行时。

auto func = [pw = std::move(pw)]        //使用std::move(pw)初始化闭包数据成员
            { return pw->isValidated()
                     && pw->isArchived(); };

auto func = [pw = std::make_unique<Widget>()]   //使用调用make_unique得到的结果
            { return pw->isValidated()          //初始化闭包数据成员
                     && pw->isArchived(); };
auto f = [](auto&& param){return func(std::forward<decltype(param)>(param));};

auto f = [](auto&&... param){return func(std::forward<decltype(param)>(param)...);};
// lambda
auto setSoundL = [](Sound s) {
        using namespace std::chrono;
        using namespace std::literals;      //对于C++14后缀

        setAlarm(steady_clock::now() + 1h,	//C++14写法,但是含义同上
                 s,
                 30s);
    };

// bind
using SetAlarm3ParamType = void(*)(Time t, Sound s, Duration d);

auto setSoundB = std::bind(
	static_cast<SetAlarm3ParamType>(setAlarm), // 无法处理类型重载,需要显式制定类型
    std::bind(std::plus<>(), steady_clock::now(), 1h), // 需要推迟表达式求值,避免在调用实际函数前求值
    _1,
	30s);

Concurrency API

Concurrency support library (since C++11) – cppreference.com

concurrency library

atomic & mutex

atomic & volatile

thread, pthread & cpu cores

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

thread & future

thread disconstruction

析构joinable的thread会导致程序终止

使用RAII(“Resource Acquisition Is Initialization,资源获得即初始化)包装thread,在析构时进行join。

class ThreadRAII {
public:
    enum class DtorAction { join, detach };
    
    ThreadRAII(std::thread&& t, DtorAction a)
    : action(a), t(std::move(t)) {}

    ~ThreadRAII()
    {
        if (t.joinable()) {
            if (action == DtorAction::join) {
                t.join();
            } else {
                t.detach();
            }
        }
    }

    std::thread& get() { return t; }

private:
    DtorAction action;
    std::thread t;
};

future disconstruction

image.png
析构future会导致隐式的join或隐式的detach:

thread communication: condition variable,atomic & future

  1. **condition variable + mutex:**需要结合互斥锁,且需要重复验证避免虚假唤醒
std::condition_variable cv;         //事件条件变量
std::mutex m;                       //配合cv使用的mutex//检测事件
cv.notify_one();                    //通知反应任务//反应的准备工作
{                                       //开启关键部分

    std::unique_lock<std::mutex> lk(m); //锁住互斥锁
    cv.wait(lk);                        //释放锁后等待通知通知后再次加锁//对事件进行反应(m已经上锁)
}                                       //关闭关键部分通过lk的析构函数解锁m//继续反应动作(m现在未上锁)
  1. **atomic:**基于轮询,而不是阻塞
std::atomic<bool> flag(false);          //共享flag//检测某个事件
flag = true;                            //告诉反应线程//准备作出反应
while (!flag);                          //等待事件//对事件作出反应
  1. **future:**使用了堆内存存储共享状态,只能使用一次通信
std::promise<void> p;                   //通信信道的promise//检测某个事件
p.set_value();                          //通知反应任务//准备作出反应
p.get_future().wait();                  //等待对应于p的那个future//对事件作出反应

原文地址:https://blog.csdn.net/qq_39384184/article/details/134754298

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_34710.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注