本文介绍: 使用RAII(“Resource Acquisition Is Initialization,资源获得即初始化)包装thread,在析构时进行join。和闭包类存在于编译期,闭包存在于运行时。
Effective Modern C++(C++14)
GitHub – CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C++》- 完成翻译
Deducing Types
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> // 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&,所以f2返回int&
}
template<typename T> //只对TD进行声明
class TD; //TD == "Type Displayer"
// 如果尝试实例化这个类模板就会引出一个错误消息,因为这里没有用来实例化的类模板定义。为了查看x和y的类型,只需要使用它们的类型去实例化TD:
TD<decltype(x)> xType; //引出包含x和y
TD<decltype(y)> yType; //的类型的错误消息
template<typename T>
void f(const T& 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& 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
Widget w1; //调用默认构造函数
Widget w2 = w1; //不是赋值运算,调用拷贝构造函数
w1 = w2; //是赋值运算,调用拷贝赋值运算符(copy operator=)
- 初始化:(),=,{}
- std::initializer_list 和 {} 初始化永远是最佳匹配
- () 初始化和 {} 初始化可能会有巨大不同:ex:std::vector
- 重载指针参数和整型参数:
- **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;
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);
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
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;
};
bool isLucky(int number); //原始版本
bool isLucky(char) = delete; //拒绝char
bool isLucky(bool) = delete; //拒绝bool
bool isLucky(double) = delete; //拒绝float和double
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; //但是已经被删除了
class Widget {
public:
using DataType = std::vector<double>;
DataType& data() & //对于左值Widgets,
{ return values; } //返回左值
DataType data() && //对于右值Widgets,
{ return std::move(values); } //返回右值
private:
DataType values;
};
- 优先使用const_iterator而非iterator
- **begin,end,cbegin,rbegin的非成员函数通用性更强,**可用于原生数组
- **尽可能使用noexcept:**可以编译优化,noexcept函数可以调用异常中立函数
RetType function(params) noexcept; //极尽所能优化 //C++11风格,没有来自f的异常
RetType function(params) throw(); //较少优化 //C++98风格,没有来自f的异常
RetType function(params); //较少优化
- **const 常量,编译期不一定可知;constexpr 编译期常量,编译期可知;constexpr 函数值根据参数在编译期是否可知决定,**constexpr函数既可以是运行时也可以是编译期
- const成员函数中的mutable成员变量,需要mutex或atomic来保证线程安全
- 特殊成员函数:构造,析构,拷贝构造,拷贝赋值,移动构造,移动赋值
- **Role of Three:**自定义资源管理时,析构,拷贝构造,拷贝赋值同时自定义
- 移动操作仅当类没有显式声明移动操作,拷贝操作,析构函数时才自动生成
- 在自定义移动时,相应拷贝被deleted,在自定义析构时,拷贝不自动生成
- **置入emplace_back优于插入push_back:**避免了临时对象的创建和销毁
Smart Pointers
- **unique_ptr:专有所有权,大小与原始指针相同,不占用额外内存资源,**完全拥有指向内容的所有权,只能移动不能复制,可以转化为shared_ptr
- **shared_ptr:共享所有权,大小是原始指针二倍,包含引用计数,**引用计数在堆上,内存动态分配,原子操作增减
- **weak_ptr:从shared_ptr创建,不拥有所有权,**只能判断原指针是否过期,不能解引用
- make_shared, make_unique:
// 原 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
- **move:**无条件将实参转化为右值,不一定移动构造,const变量无法移动时只能拷贝构造
- **forward:**当且仅当实参为右值时,将其转化为右值
- move 和 forward 都是** cast 操作,运行期无代码**
- **通用引用:**T&&, auto&&,类型推导决定是左值引用还是右值引用
- 函数返回值,传参,最后一次使用:右值引用使用move,通用引用使用forward
- **返回值优化:**返回函数内的局部对象
- 通用引用参数的函数重载,优先级高于大部分函数
- 完美转发的构造函数,non-const的优先级可能会大于拷贝构造和系统构造函数
- 重载通用引用:
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);
…
};
- 引用折叠(reference collapsing)。禁止你声明引用的引用,但是编译器会在特定的上下文中产生这些。当编译器生成引用的引用时,引用折叠指导下一步发生什么。
- **移动语义的缺陷:**某些情况不存在,速度不如复制,非noexcept移动不可用
- 完美转发(perfect forwarding)意味着我们不仅转发对象,我们还转发显著的特征:它们的类型,是左值还是右值,是const还是volatile。
- **完美转发失败:**花括号初始化,作为空指针的0或者NULL,仅有声明的整型static const数据成员,模板和重载函数的名字,位域。
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
- **按引用捕获:**捕获的变量超出生命周期,导致悬空引用
- **按值捕获【默认】:**捕获指针,导致悬空引用;无法捕捉成员变量,只能捕捉this指针;无法捕捉static变量
- **初始化捕获,可以捕获移动对象【移动捕获】:**c++11可以使用bind来模拟捕获移动对象
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:**原子操作,一个CPU指令实现
- **mutex:**互斥锁,可由atomic实现加锁解锁操作,未拿到锁需要陷入内核完成线程挂起
- **自旋锁:**可由atomic实现,未拿到锁不会挂起线程,效率高于互斥锁
atomic & volatile
thread, pthread & cpu cores
thread & future
thread disconstruction
使用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
析构future会导致隐式的join或隐式的detach:
thread communication: condition variable,atomic & future
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现在未上锁)
std::atomic<bool> flag(false); //共享的flag
… //检测某个事件
flag = true; //告诉反应线程
… //准备作出反应
while (!flag); //等待事件
… //对事件作出反应
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进行投诉反馈,一经查实,立即删除!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。