现代C++——基础语法

第一章:现代C++基础语法增强

📚 章节概述

本章介绍现代C++(C++11及以后版本)中对基础语法的重要增强。这些特性让C++代码更加简洁、安全和高效。掌握这些基础特性是学习后续高级特性的重要基础。

🎯 学习目标

学完本章后,您将能够:

  • 使用auto关键字进行自动类型推导
  • 掌握范围for循环的各种用法
  • 理解并使用统一初始化语法
  • 正确使用nullptr替代传统的NULL
  • 理解右值引用和移动语义的基础概念
  • 使用constexpr进行编译时计算
  • 运用static_assert进行编译时断言
  • 掌握decltype类型推导
  • 使用委托构造函数简化类设计
  • 理解=default=delete的用途

1. auto关键字 - 自动类型推导

📖 理论讲解

auto关键字让编译器根据初始化表达式自动推导变量类型,减少了代码冗余,提高了代码的可维护性。

💡 核心概念

  • 类型推导:编译器根据右侧表达式推导左侧变量类型
  • 简化声明:避免书写复杂的类型名称
  • 模板友好:在模板代码中特别有用

🔍 使用场景

// 1. 简单类型推导
auto x = 42;        // int
auto y = 3.14;      // double
auto name = "C++";  // const char*

// 2. 复杂类型推导
std::vector<std::string> vec = {"hello", "world"};
auto it = vec.begin();  // std::vector<std::string>::iterator

// 3. 函数返回值推导
auto result = some_complex_function();

// 4. Lambda表达式
auto lambda = [](int a, int b) { return a + b; };

⚠️ 注意事项

  • auto必须立即初始化
  • 推导的是值类型,不是引用类型
  • 对于引用,需要显式使用auto&
  • 对于指针,autoauto*效果相同

🎯 最佳实践

  1. 使用场景:当类型名很长或很复杂时使用
  2. 避免滥用:对于简单类型,明确写出类型更清晰
  3. 配合IDE:现代IDE会显示推导出的类型

2. 范围for循环 (Range-based for loop)

📖 理论讲解

范围for循环提供了一种简洁的方式来遍历容器和数组,减少了传统for循环的样板代码。

💡 核心概念

  • 简化语法for (element : container)
  • 自动迭代:编译器自动处理迭代器
  • 类型推导:可以配合auto使用

🔍 使用场景

// 1. 遍历容器
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (const auto& num : numbers) {
    std::cout << num << " ";
}

// 2. 修改元素
for (auto& num : numbers) {
    num *= 2;  // 修改原始元素
}

// 3. 遍历数组
int arr[] = {1, 2, 3, 4, 5};
for (int value : arr) {
    std::cout << value << " ";
}

// 4. 带索引的遍历(C++20风格)
for (auto [index, value] : enumerate(numbers)) {
    std::cout << index << ": " << value << std::endl;
}

⚠️ 注意事项

  • 使用const auto&避免不必要的拷贝
  • 使用auto&当需要修改元素时
  • 不要在循环中修改容器结构

3. 初始化列表 (Initializer List)

📖 理论讲解

统一初始化语法提供了一致的初始化方式,使用花括号{}来初始化各种类型的对象。

💡 核心概念

  • 统一语法:所有类型都可以用{}初始化
  • 防止窄化转换:编译时检查类型安全
  • 初始化列表构造:支持std::initializer_list

🔍 使用场景

// 1. 基本类型初始化
int x{42};
double y{3.14};

// 2. 容器初始化
std::vector<int> vec{1, 2, 3, 4, 5};
std::map<std::string, int> m{{"apple", 1}, {"banana", 2}};

// 3. 数组初始化
int arr[]{1, 2, 3, 4, 5};

// 4. 类对象初始化
class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
};
Point p{10, 20};

// 5. 防止窄化转换
int safe{3.14};  // 编译错误!防止double到int的窄化

🎯 最佳实践

  1. 优先使用:尽量使用统一初始化语法
  2. 避免歧义:注意{}()的区别
  3. 类型安全:利用防止窄化转换的特性

4. nullptr - 空指针字面值

📖 理论讲解

nullptr是C++11引入的空指针字面值,用来替代传统的NULL,提供了类型安全的空指针表示。

💡 核心概念

  • 类型安全nullptr有自己的类型std::nullptr_t
  • 重载友好:能正确匹配指针重载函数
  • 现代标准:是现代C++的推荐做法

🔍 使用场景

// 1. 指针初始化
int* ptr = nullptr;

// 2. 函数重载区分
void func(int value);
void func(int* ptr);

func(0);        // 调用func(int),可能不是想要的
func(nullptr);  // 明确调用func(int*)

// 3. 条件判断
if (ptr != nullptr) {
    // 安全使用ptr
}

// 4. 返回值
int* find_element() {
    // 未找到时返回nullptr
    return nullptr;
}

⚠️ 注意事项

  • 不要混用NULLnullptr
  • nullptr可以转换为任何指针类型
  • 不能直接转换为整数类型

5. 右值引用和移动语义基础

📖 理论讲解

右值引用(&&)是C++11引入的新特性,支持移动语义,可以避免不必要的拷贝操作,提高程序性能。

💡 核心概念

  • 左值 vs 右值:左值有名字且可寻址,右值是临时对象
  • 移动语义:转移资源所有权而非拷贝
  • 完美转发:保持参数的值类别

🔍 使用场景

// 1. 移动构造函数
class Resource {
private:
    int* data;
    size_t size;
    
public:
    // 移动构造函数
    Resource(Resource&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }
    
    // 移动赋值运算符
    Resource& operator=(Resource&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }
};

// 2. std::move的使用
Resource r1(100);
Resource r2 = std::move(r1);  // 移动而非拷贝

🎯 最佳实践

  1. 实现移动语义:为资源管理类实现移动构造和移动赋值
  2. 使用std::move:明确表示移动意图
  3. noexcept标记:移动操作应该标记为noexcept

6. constexpr - 编译时常量表达式

📖 理论讲解

constexpr关键字表示表达式或函数可以在编译时求值,提供了编译时计算的能力。

💡 核心概念

  • 编译时计算:在编译期间计算结果
  • 性能优化:避免运行时计算开销
  • 模板友好:可用作模板参数

🔍 使用场景

// 1. constexpr变量
constexpr int max_size = 100;
constexpr double pi = 3.14159;

// 2. constexpr函数
constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

// 3. 编译时计算
constexpr int result = factorial(5);  // 编译时计算

// 4. constexpr构造函数
class Point {
    int x_, y_;
public:
    constexpr Point(int x, int y) : x_(x), y_(y) {}
    constexpr int x() const { return x_; }
    constexpr int y() const { return y_; }
};

constexpr Point origin(0, 0);

⚠️ 注意事项

  • constexpr函数必须能在编译时求值
  • 不是所有调用都必须在编译时进行
  • C++14放宽了constexpr函数的限制

7. static_assert - 编译时断言

📖 理论讲解

static_assert提供编译时断言功能,可以在编译期间检查条件,如果条件不满足则产生编译错误。

💡 核心概念

  • 编译时检查:在编译阶段进行断言
  • 模板安全:确保模板参数满足要求
  • 平台检查:检查平台相关的假设

🔍 使用场景

// 1. 基本断言
static_assert(sizeof(int) == 4, "int must be 4 bytes");

// 2. 模板约束
template<typename T>
class MyClass {
    static_assert(std::is_arithmetic_v<T>, "T must be arithmetic type");
    // ...
};

// 3. 编译时条件检查
constexpr bool is_64bit = sizeof(void*) == 8;
static_assert(is_64bit, "This code requires 64-bit platform");

// 4. C++17简化语法
static_assert(sizeof(long) >= 8);  // 可以省略错误消息

🎯 最佳实践

  1. 提供清晰错误信息:写明断言失败的原因
  2. 模板中使用:确保模板参数类型正确
  3. 平台适配:检查平台相关假设

8. decltype - 类型推导

📖 理论讲解

decltype关键字可以推导表达式的类型,常用于模板编程和复杂类型声明。

💡 核心概念

  • 表达式类型推导:根据表达式推导类型
  • 保持类型属性:保持const、引用等属性
  • 模板编程:在模板中推导类型

🔍 使用场景

// 1. 基本用法
int x = 42;
decltype(x) y = x;  // y的类型是int

// 2. 复杂表达式
std::vector<int> vec;
decltype(vec.begin()) it = vec.begin();

// 3. 函数返回类型推导
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// 4. 变量模板
template<typename T>
decltype(auto) get_value() {
    static T value{};
    return value;
}

⚠️ 注意事项

  • decltype保持引用和const属性
  • 对于变量名,decltype返回变量类型
  • 对于表达式,decltype可能返回引用类型

9. 委托构造函数

📖 理论讲解

委托构造函数允许一个构造函数调用同一个类的另一个构造函数,减少代码重复。

💡 核心概念

  • 代码复用:避免构造函数中的重复代码
  • 初始化链:构造函数可以委托给其他构造函数
  • 清晰设计:让类的初始化逻辑更清晰

🔍 使用场景

class Circle {
private:
    double radius_;
    std::string color_;
    
public:
    // 主构造函数
    Circle(double radius, const std::string& color) 
        : radius_(radius), color_(color) {
        std::cout << "Circle created: radius=" << radius_ 
                  << ", color=" << color_ << std::endl;
    }
    
    // 委托构造函数
    Circle() : Circle(1.0, "red") {}  // 委托给主构造函数
    
    Circle(double radius) : Circle(radius, "blue") {}  // 委托给主构造函数
    
    Circle(const std::string& color) : Circle(1.0, color) {}  // 委托给主构造函数
};

🎯 最佳实践

  1. 避免循环委托:确保委托链不形成循环
  2. 选择合适的主构造:让最完整的构造函数作为主构造函数
  3. 简化初始化:用委托减少重复的初始化代码

10. =default 和 =delete

📖 理论讲解

=default=delete提供了显式控制编译器生成特殊成员函数的方法。

💡 核心概念

  • =default:显式要求编译器生成默认实现
  • =delete:显式禁止某个函数
  • RAII安全:更好地控制对象的生命周期

🔍 使用场景

class NonCopyable {
public:
    NonCopyable() = default;  // 使用编译器生成的默认构造函数
    
    // 禁止拷贝
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    
    // 允许移动
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
    
    ~NonCopyable() = default;
};

class Singleton {
private:
    Singleton() = default;
    
public:
    // 禁止拷贝和移动
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;
    
    static Singleton& instance() {
        static Singleton inst;
        return inst;
    }
};

🎯 最佳实践

  1. 明确意图:使用=default明确表示使用默认实现
  2. 禁用危险操作:使用=delete禁止不安全的操作
  3. RAII设计:配合智能指针设计更安全的类

🧪 实践练习

练习1:auto的使用

// 使用auto简化以下代码
std::vector<std::map<std::string, int>>::iterator it = container.begin();
std::function<int(int, int)> calculator = [](int a, int b) { return a + b; };

练习2:范围for循环

// 将传统for循环改写为范围for循环
std::vector<std::string> words = {"hello", "modern", "cpp"};
for (size_t i = 0; i < words.size(); ++i) {
    std::cout << words[i] << std::endl;
}

练习3:移动语义

// 为以下类实现移动构造函数和移动赋值运算符
class Buffer {
private:
    char* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new char[s]) {}
    ~Buffer() { delete[] data; }
    // 添加移动构造函数和移动赋值运算符
};

📝 本章小结

本章介绍了现代C++的基础语法增强,这些特性让C++代码更加:

  • 简洁auto和范围for循环减少了样板代码
  • 安全nullptrstatic_assert提供了更好的类型安全
  • 高效:移动语义避免了不必要的拷贝
  • 表达力强:统一初始化和委托构造函数让代码意图更清晰

这些基础特性是学习后续高级特性的重要基础,建议通过大量练习来掌握它们的使用场景和最佳实践。


🔗 相关资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值