c++17 注解标签 attributes & c++ 枚举值 & 结构化绑定
c++ 标注
c++17 后逐渐完善
注解标签语法:[[attribute]] types/functions/enums/etc
- 告诉编译器没有返回值 [[noreturn]]
常用于系统函数设计,如 std::abort() std::exit();[[noreturn]] void terminate();
- [[deprecated]] 标注函数或类型已被弃用
[[deprecated]] void funcX(); [[deprecated("use funY instead")]] void funcX();
- [[fallthrough]] 当switch-case 分支完毕后没有break时,取消警告
switch(type) { case 1: fun1(); // 这里没有break,所以编译器会给出警告 [[fallthrough]]; // g++中无需分号,vs中必须有分号 case 2: fun2(); // 因为有 [[fallthrough]] 所以编译器不会警告 }
- [[nodiscard]] 修饰函数,返回值必须被使用
[[nodiscard]] int fun() { return 0; } int main() { fun(); // 此处没有变量接收返回值,编译器给出警告 return 0; }
c++20 中对于 operator new() std::allocate() 等库函数均使用了 [[nodiscard]] 进行标记
- [[maybe_unused]] 标注函数或变量不再被使用
int WINAPI wWinMain(HINSTANCE hInstance, [[maybe_unused]] HINSTANCE hPrevInstance, [[maybe_unused]] LPWSTR lpCmdLine, int nCmdShow) { // ... }
c++ 枚举
/***************** c++98/03 enumeration******************/
enum Color {
black,
white
};
bool white = true; // 编译不通过,与枚举值冲突
/******************** c++11 enumerator ********************/
enum class Color {
black,
white
};
bool white = true; // 编译通过,枚举值外部不可见,必须通过 Color::white 引用
结构化绑定
绑定名词时绑定的是目标的拷贝,当绑定目标不是基础数据类型时,建议使用引用。如不需修改,使用const
// map的insert方法返回 std::pair<迭代器, 是否成功的布尔值>
// 使用结构化绑定处理返回值
auto [iterator, inserted] = m.insert(std::pair(2, "2"));
// 接收数组成员
int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;
auto& [d, e, f] = arr;
// 接收类成员
struct Point {
int x;
int y;
};
Point p{1, 2};
auto [x, y] = p;
const auto& [xx, yy] = p;
for (cont auto& [name, value] : map_) {
std::cout << name << " : " << value << std::endl;
}
用于结构化绑定的变量不能使用 constexpr 修饰或声明为 static。有些编译器也不支持在lambda中捕获使用结构化绑定的语法。
原位构造元素 emplace/emplace_front
在 list vector 中都有
std::list<Test> coll;
// c++11 之前,一次循环中会调用1次Test的构造函数、1次Test的析构函数、1次拷贝构造函数
for (int i = 0; i < 10; ++i) {
Test t{i, 2*i, 3*i};
coll.push_back(t);
}
// c++11 语法,只调用一次Test的构造函数
for (int i = 0; i < 10; ++i) {
coll.emplace_back(i, 2*i, 3*i);
}
map 的 try_emplace 方法
try_emplace :如果key存在则什么都不做,不存在则在指定位置插入key并构造value对象并插入
智能指针的 enable_shared_from_this 模板对象
当类需要返回一个包裹当前对象的一个 std::shared_pt 对象给外部使用时,只要继承 std::enable_shated_from_this<T>
模板对象即可。继承后得到 getSelf()
方法,内部使用了 shared_from_this 方法。
注意事项:
- 不该共享栈对象的this指针给sp对象
- 循环引用计数问题
class A : public std::enable_shared_from_this<A> { public: A() { std::cout << "ctor" << std::endl; } ~A() { std::cout << "dtor" << std::endl; } void fun() { // 如果在构造函数中赋值则会直接崩溃,构造函数中无this self_ = shared_from_this(); } private: std::shared_ptr<A> self_; }; int main(int argc, char const *argv[]) { std::shared_ptr<A> spa = std::make_shared<A>(); spa->fun(); return 0; }
程序生命周期内不会调用析构函数,因为对象的循环引用
weak_ptr
weak_ptr 用于协助 shared_ptr,如避免循环引用。它不会增加计数值。weak_ptr 不管理资源生命周期,所以引用资源可能在某时失效。
使用 expired 方法检测资源是否有效,使用 lock 方法将指针升级为 shared_ptr。
因没有重写 operator-> operator* 方法,所以不能直接操作资源。
多线程场景下,要对lock方法加锁。如果可用就使用,若不可用就不参与生命周期的管理。
经典引用场景就是订阅者或观察者模式
智能指针大小
一个 std::unique_ptr 大小和裸指针大小相同,std::shared_ptr 和 std::weak_ptr 大小是 std::unique_ptr 的两倍。
智能指针注意事项
- 使用智能指针管理对象后,就不该再使用原始指针操作它
- 不需要共享资源时使用 std::unique_ptr,无需管理生命周期时使用 std::weak_ptr
- 避免操作某个资源已经释放的智能指针
- 作为类成员变量,应该优先使用前置声明
// 优先使用A的前置声明,而不是直接包含 A.h 文件 class A; class Test { private: std::unique_ptr<A> spA_; };