[C++11] : atomic,nullptr,default/delete,enum class

C++11 四大特性精讲

    • [一、std::atomic 原子操作](#一、std::atomic 原子操作)
      • [1. 为什么普通 int 加法线程不安全](#1. 为什么普通 int 加法线程不安全)
        • [错误示例(普通 int 多线程累加)](#错误示例(普通 int 多线程累加))
      • [2. std::atomic<int> 原子加法(线程安全)](#2. std::atomic<int> 原子加法(线程安全))
        • [基础用法:原子自增 cnt++、原子加法 cnt += n](#基础用法:原子自增 cnt++、原子加法 cnt += n)
      • [3. 原子操作配套接口(`load` / `store`)](#3. 原子操作配套接口(load / store))
      • [4. 关键约束](#4. 关键约束)
    • 二、nullptr标准空指针
      • [1. 历史问题:0 和 NULL](#1. 历史问题:0 和 NULL)
      • [2. nullptr 核心特性](#2. nullptr 核心特性)
      • [3. 重载场景对比(最经典案例)](#3. 重载场景对比(最经典案例))
      • [4. 日常标准用法](#4. 日常标准用法)
      • [5. 总结规范](#5. 总结规范)
    • [三、=default / =delete 控制默认成员函数](#三、=default / =delete 控制默认成员函数)
    • [四、enum class强类型枚举](#四、enum class强类型枚举)
      • [1. 传统 enum的两大缺陷](#1. 传统 enum的两大缺陷)
      • [2. enum class 强枚举语法 & 核心特性](#2. enum class 强枚举语法 & 核心特性)
      • [3. 枚举比较规则](#3. 枚举比较规则)
  • 综合速记总结

一、std::atomic 原子操作

头文件:#include <atomic>

1. 为什么普通 int 加法线程不安全

i++ / i += n 看似一行代码,底层会拆成三条CPU指令 :读内存 → 运算 → 写回内存。

多线程交替执行时,指令被打断,就会出现数据丢失

错误示例(普通 int 多线程累加)
cpp 复制代码
#include <iostream>
#include <thread>

// 普通整型,非原子
int cnt = 0;

// 线程函数:循环累加 100000 次
void add()
{
    for (int i = 0; i < 100000; ++i)
    {
        cnt += 1; // 非原子加法,线程不安全
    }
}

int main()
{
    // 开启两个线程同时累加
    std::thread t1(add);
    std::thread t2(add);

    t1.join();
    t2.join();

    // 预期结果:200000,实际大概率小于 200000
    std::cout << "普通int累加结果:" << cnt << std::endl;
    return 0;
}

原因:两个线程同时读取到相同的 cnt 值,各自+1后写回,一次加法被覆盖


2. std::atomic 原子加法(线程安全)

std::atomic把读写、自增、加减赋值 全部封装为硬件原子指令,整个操作不可中断。

基础用法:原子自增 cnt++、原子加法 cnt += n
cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>

// 原子整型
std::atomic<int> cnt{0};

void add()
{
    for (int i = 0; i < 100000; ++i)
    {
        cnt += 1;  // 原子加法:线程安全
        // cnt++;   // 等价,也是原子操作
    }
}

int main()
{
    std::thread t1(add);
    std::thread t2(add);

    t1.join();
    t2.join();

    // 结果一定是 200000
    std::cout << "atomic<int> 累加结果:" << cnt << std::endl;
    return 0;
}

3. 原子操作配套接口(load / store

  • load()原子读,读取原子变量的值
  • store(val)原子写,向原子变量写入值
cpp 复制代码
std::atomic<int> num{10};

int a = num.load();   // 原子读取,a = 10
num.store(20);        // 原子写入,num = 20

num += 5;             // 原子加法,num = 25
int b = num.load();   // b = 25

4. 关键约束

  1. std::atomic<T> 禁止拷贝构造、拷贝赋值

    cpp 复制代码
    std::atomic<int> x{0};
    // std::atomic<int> y = x;  // 编译报错,不能拷贝
  2. 仅保证单一原子变量 操作安全,多行代码组合依然有竞争

    cpp 复制代码
    std::atomic<int> a{0}, b{0};
    if (a > 0) b += 1; // 整体逻辑非原子,仍存在线程安全问题
  3. 只支持内置类型、指针,不支持自定义类。


二、nullptr标准空指针

1. 历史问题:0 和 NULL

C++ 中 #define NULL 0,本质是整型 0,和真正的空指针类型割裂。

2. nullptr 核心特性

  • C++11 关键字,专属空指针类型 std::nullptr_t
  • 可隐式转为任意指针,不能隐式转为整型
  • 彻底解决函数重载歧义

3. 重载场景对比(最经典案例)

cpp 复制代码
#include <iostream>

// 整型重载
void func(int)
{
    std::cout << "匹配 int 版本" << std::endl;
}

// 指针重载
void func(void*)
{
    std::cout << "匹配 指针 版本" << std::endl;
}

int main()
{
    func(0);        // 字面量0 → 调用 int
    func(NULL);     // NULL=0 → 依旧调用 int(不符合空指针意图)
    func(nullptr);  // 纯空指针 → 精准调用 void*(正确)

    // int n = nullptr; // 编译报错:禁止转整型
    return 0;
}

4. 日常标准用法

(1)指针初始化/置空
cpp 复制代码
int* p = nullptr;
char* pc = nullptr;
void (*fp)() = nullptr; // 函数指针
p = nullptr;            // 运行时置空
(2)空指针判断
cpp 复制代码
if (p == nullptr)
{
    // 指针为空,推荐写法,语义清晰
}

if (!p)
{
    // 简写写法,语法合法
}

5. 总结规范

新项目一律使用 nullptr,放弃 NULL 和裸 0 表示空指针


三、=default / =delete 控制默认成员函数

C++ 类有 6 个编译器默认生成的特殊成员函数:

默认构造、析构、拷贝构造、拷贝赋值、移动构造、移动赋值。

1. = default:显式使用编译器默认实现

作用

手动写了有参构造 后,编译器不再生成默认构造,用 =default 恢复默认构造。

示例
cpp 复制代码
class Student
{
public:
    // 显式让编译器生成 默认无参构造
    Student() = default;

    // 自定义有参构造
    Student(int id)
    {
        std::cout << "有参构造" << std::endl;
    }
};

int main()
{
    Student s1;    // 合法:调用 default 默认构造
    Student s2(1); // 合法:调用有参构造
    return 0;
}

拓展:也可用于析构、拷贝、移动函数,统一使用编译器默认逻辑。


2. = delete:删除函数(禁用调用)

调用被 delete 修饰的函数,直接编译报错,用于限制对象行为。

场景1:禁用默认构造(无法无参创建对象)
cpp 复制代码
class A
{
public:
    A() = delete;  // 删除默认构造
    A(int x) {}
};

// A a;      // 编译报错:默认构造已被删除
A b(10);     // 合法
场景2:禁用拷贝构造 + 拷贝赋值(不可拷贝类)

常用于单例、独占资源类,禁止对象被拷贝:

cpp 复制代码
class NoCopy
{
public:
    NoCopy() = default;

    // 删除拷贝构造
    NoCopy(const NoCopy&) = delete;
    // 删除拷贝赋值
    NoCopy& operator=(const NoCopy&) = delete;
};

NoCopy obj1;
// NoCopy obj2 = obj1; // 编译报错,拷贝被禁用
// obj1 = obj2;        // 编译报错,赋值被禁用
场景3:禁用普通重载函数

拦截非法参数传入:

cpp 复制代码
void test(int) {}
void test(double) = delete;

// test(3.14); // 编译报错:double 版本被删除

四、enum class强类型枚举

1. 传统 enum的两大缺陷

  1. 作用域泄露:枚举常量直接暴露在当前作用域,容易重名
  2. 隐式转 int:枚举和整型随意混用,类型不安全
cpp 复制代码
// 传统弱枚举(不推荐)
enum Color { Red, Green, Blue };
// enum Light { Red, Yellow }; // 编译报错:Red 重名冲突

int a = Red;  // 隐式转为 int,类型无检查
Color c = 1;  // 整型直接赋值给枚举,非法值也能通过

2. enum class 强枚举语法 & 核心特性

语法格式:

cpp 复制代码
enum class 枚举名 [: 底层类型]
{
    常量1,
    常量2 = 自定义值
};
特性1:独立作用域,必须 枚举名:: 访问
cpp 复制代码
enum class Color
{
    Red,
    Green,
    Blue
};

int main()
{
    // Color c = Red;  // 报错:Red 不在当前作用域
    Color c = Color::Red; // 正确写法
    return 0;
}
特性2:禁止隐式转 int,类型严格安全
cpp 复制代码
enum class Color : int
{
    Red = 1,
    Green,
    Blue
};

Color c = Color::Green;
// int val = c;  // 编译报错:不允许隐式转整型

// 如需转整型,必须显式强转
int val = static_cast<int>(c);
特性3:解决命名冲突

多个枚举可以拥有同名常量:

cpp 复制代码
enum class Color { Red, Green };
enum class Light { Red, Yellow };

Color c = Color::Red;
Light l = Light::Red; // 完全合法,作用域隔离
特性4:自定义底层存储类型

可指定 char/short/unsigned int 节省内存:

cpp 复制代码
// 底层为 char,仅占 1 字节
enum class Status : char
{
    Off = 0,
    On  = 1
};

3. 枚举比较规则

  • 同一种强枚举之间可以正常比较
  • 枚举 和 int、不同枚举之间不能直接比较
cpp 复制代码
enum class Color { Red, Green };

Color c1 = Color::Red;
Color c2 = Color::Green;

if (c1 != c2) {}       // 合法
// if (c1 == 0) {}     // 报错:不能和整型比较

综合速记总结

  1. std::atomic<int>
    专门解决多线程下 int 自增/加法的数据竞争,操作原子不可中断,无需手动加锁。
  2. nullptr
    标准空指针,替代 NULL/0,解决重载歧义,类型安全。
  3. =default / =delete
    =default:启用编译器默认成员函数;
    =delete:禁用函数调用,编译期拦截非法操作。
  4. enum class
    强类型枚举,作用域隔离、禁止隐式转整型,比传统 enum 更安全,工程开发首选。
相关推荐
01_ice1 小时前
C语言数据在内存中的存储
c语言·开发语言
代码村新手1 小时前
C++-二叉搜索树
开发语言·c++
智者知已应修善业2 小时前
【51单片机8位数码管动态显示日期小数点风格】2023-11-13
c++·经验分享·笔记·算法·51单片机
智者知已应修善业2 小时前
【51单片机有三个LED 分别第一个灯闪三下 再到第二个灯又闪三下 再到第三个灯又闪三下 就这样循环程序】2023-11-16
c++·经验分享·笔记·算法·51单片机
吃好睡好便好3 小时前
创建魔方矩阵和单位矩阵
开发语言·人工智能·学习·线性代数·matlab·矩阵
影寂ldy3 小时前
C#数组的属性和方法(Clear / Copy / IndexOf )
开发语言·javascript·c#
i7i8i9com3 小时前
Hermes Agent 安装记录
开发语言·bash·hermes
小娄~~4 小时前
C语言卷子错题集
c语言·开发语言·数据结构
一然明月5 小时前
qt基本用法
开发语言·qt