c++17 注解标签 attributes & c++ 枚举值 & 结构化绑定

c++ 标注

c++17 后逐渐完善

注解标签语法:[[attribute]] types/functions/enums/etc 

  1. 告诉编译器没有返回值 [[noreturn]]
    常用于系统函数设计,如 std::abort() std::exit();
    [[noreturn]] void terminate();
  2. [[deprecated]] 标注函数或类型已被弃用
    [[deprecated]] void funcX();
    
    [[deprecated("use funY instead")]] void funcX();
  3. [[fallthrough]] 当switch-case 分支完毕后没有break时,取消警告
    switch(type) {
        case 1:
            fun1();  // 这里没有break,所以编译器会给出警告
        [[fallthrough]]; // g++中无需分号,vs中必须有分号
        case 2:
            fun2();  // 因为有 [[fallthrough]] 所以编译器不会警告
    }
  4. [[nodiscard]] 修饰函数,返回值必须被使用
    [[nodiscard]] int fun() { return 0; }
    
    int main() {
      fun();   // 此处没有变量接收返回值,编译器给出警告
      return 0;
    }

    c++20 中对于 operator new()  std::allocate() 等库函数均使用了 [[nodiscard]] 进行标记

  5. [[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 方法。

注意事项:

  1. 不该共享栈对象的this指针给sp对象
  2. 循环引用计数问题
    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 的两倍。

智能指针注意事项

  1. 使用智能指针管理对象后,就不该再使用原始指针操作它
  2. 不需要共享资源时使用 std::unique_ptr,无需管理生命周期时使用 std::weak_ptr
  3. 避免操作某个资源已经释放的智能指针
  4. 作为类成员变量,应该优先使用前置声明
    // 优先使用A的前置声明,而不是直接包含 A.h 文件
    class A;
    
    class Test {
     private:
      std::unique_ptr<A> spA_;
    };

 

posted @ 2022-10-29 11:45  某某人8265  阅读(79)  评论(0编辑  收藏  举报