C++学习:六个月从根本到就业——C++11/14:其他语言特性 ...

打印 上一主题 下一主题

主题 1872|帖子 1872|积分 5616

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
C++学习:六个月从根本到就业——C++11/14:其他语言特性

   本文是我C++学习之旅系列的第四十四篇技术文章,也是第三阶段"今世C++特性"的第六篇,主要介绍C++11/14中引入的其他重要语言特性。检察完整系列目次了解更多内容。
  引言

在前几篇文章中,我们已经详细介绍了C++11/14引入的一些主要特性,如右值引用与移动语义、lambda表达式、auto类型推导、decltype关键字以及列表初始化。然而,C++11/14尺度还引入了许多其他重要的语言特性,这些特性固然不像前面几个那样被广泛讨论,但在一样平常编程中同样非常有用。
本文将介绍这些"其他语言特性",包括nullptr、constexpr、范围for循环、默认函数控制、罗列类等。这些特性共同构成了今世C++的根本,掌握它们对于编写更简便、更安全、更高效的C++代码至关重要。
目次



  • nullptr关键字
  • constexpr关键字
  • 范围for循环
  • 默认函数控制
  • override和final关键字
  • 强类型罗列
  • 用户定义字面量
  • noexcept阐明符
  • thread_local存储类型
  • 内联命名空间
  • 总结
nullptr关键字

NULL的题目

在C++11之前,程序员通常使用NULL宏或直接使用0来表现空指针,这可能导致函数重载解析的歧义:
  1. void foo(int i) { std::cout << "整数版本" << std::endl; }
  2. void foo(char* p) { std::cout << "指针版本" << std::endl; }
  3. int main() {
  4.     foo(0);       // 调用foo(int)
  5.     foo(NULL);    // 在大多数实现中,也会调用foo(int)
  6. }
复制代码
nullptr的优势

C++11引入了nullptr关键字,它是一个特别的字面量,可以隐式转换为任何指针类型,但不能转换为整数类型:
  1. void foo(int i) { std::cout << "整数版本" << std::endl; }
  2. void foo(char* p) { std::cout << "指针版本" << std::endl; }
  3. int main() {
  4.     foo(nullptr);  // 明确调用foo(char*)
  5.    
  6.     // 可以隐式转换为任何指针类型
  7.     int* p1 = nullptr;
  8.     char* p2 = nullptr;
  9.    
  10.     // 但不能转换为整数
  11.     // int n = nullptr;  // 编译错误
  12. }
复制代码
nullptr的类型是std::nullptr_t,可用于重载区分和模板参数匹配,提高了代码的类型安全性和可读性。
constexpr关键字

constexpr关键字允许在编译期盘算表达式的值,提供了真正的常量表达式功能。
编译期常量

  1. // 编译期常量
  2. constexpr int MAX_SIZE = 100;
  3. // 可用于数组大小
  4. constexpr int getSizeForArray() { return 42; }
  5. int array[getSizeForArray()];  // 合法:在编译期求值
复制代码
constexpr函数

C++11中的constexpr函数有许多限制,只能包含简单的代码:
  1. // C++11 constexpr函数
  2. constexpr int factorial(int n) {
  3.     return n <= 1 ? 1 : (n * factorial(n - 1));
  4. }
  5. // 编译期计算
  6. constexpr int fact5 = factorial(5);  // 编译期计算为120
复制代码
C++14中的增强

C++14大大放宽了对constexpr函数的限制,允许使用循环、条件语句等:
  1. // C++14 constexpr函数
  2. constexpr int factorial(int n) {
  3.     int result = 1;
  4.     for (int i = 1; i <= n; ++i) {
  5.         result *= i;
  6.     }
  7.     return result;
  8. }
复制代码
应用场景

constexpr的主要应用包括:

  • 编译期数学盘算
    1. constexpr double circleArea(double r) {
    2.     return 3.14159 * r * r;
    3. }
    复制代码
  • 类型特性和元编程
    1. template<typename T>
    2. constexpr bool is_power_of_two(T x) {
    3.     return x > 0 && (x & (x - 1)) == 0;
    4. }
    5. static_assert(is_power_of_two(16), "16是2的幂");
    复制代码
  • 提高性能:可编译期盘算的内容不会增加运行时开销
范围for循环

根本语法

C++11引入了范围for循环,使迭代容器和数组更加方便:
  1. // 数组迭代
  2. int arr[] = {1, 2, 3, 4, 5};
  3. for (int value : arr) {
  4.     std::cout << value << " ";
  5. }
  6. // 容器迭代
  7. std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
  8. for (const auto& name : names) {  // 使用引用避免复制
  9.     std::cout << name << " ";
  10. }
  11. // 修改元素
  12. std::vector<int> numbers = {1, 2, 3, 4, 5};
  13. for (auto& num : numbers) {
  14.     num *= 2;  // 每个元素乘以2
  15. }
复制代码
范围for循环的一样平常语法是:for (declaration : expression) { ... }
与迭代器的对比

相比传统迭代器方法,范围for循环更简便,更不易出错:
  1. // 传统迭代器方法
  2. for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
  3.     *it *= 2;
  4. }
  5. // 范围for循环
  6. for (auto& elem : vec) {
  7.     elem *= 2;
  8. }
复制代码
自定义类型支持

要使自定义类型支持范围for循环,必要提供begin()和end()函数:
  1. class IntegerRange {
  2. private:
  3.     int start;
  4.     int end;
  5.    
  6.     class Iterator {
  7.         // 实现迭代器接口...
  8.     };
  9.    
  10. public:
  11.     IntegerRange(int s, int e) : start(s), end(e) {}
  12.    
  13.     Iterator begin() const { return Iterator(start); }
  14.     Iterator end() const { return Iterator(end); }
  15. };
  16. // 使用
  17. for (int i : IntegerRange(1, 6)) {
  18.     std::cout << i << " ";  // 输出: 1 2 3 4 5
  19. }
复制代码
默认函数控制

C++11引入了=default和=delete来更精确地控制特别成员函数的天生。
default关键字

显式要求编译器天生默认版本的特别成员函数:
  1. class Example {
  2. public:
  3.     // 显式要求生成默认构造函数
  4.     Example() = default;
  5.    
  6.     // 自定义构造函数
  7.     Example(int val) : value(val) {}
  8.    
  9.     // 显式要求生成默认析构函数和拷贝操作
  10.     ~Example() = default;
  11.     Example(const Example&) = default;
  12.     Example& operator=(const Example&) = default;
  13.    
  14. private:
  15.     int value = 0;
  16. };
复制代码
delete关键字

禁止使用特定函数:
  1. class NonCopyable {
  2. public:
  3.     NonCopyable() = default;
  4.    
  5.     // 禁止拷贝
  6.     NonCopyable(const NonCopyable&) = delete;
  7.     NonCopyable& operator=(const NonCopyable&) = delete;
  8.    
  9.     // 允许移动
  10.     NonCopyable(NonCopyable&&) = default;
  11.     NonCopyable& operator=(NonCopyable&&) = default;
  12. };
  13. // 禁止特定类型的隐式转换
  14. class NoInt {
  15. public:
  16.     NoInt(double d) {}      // 允许从double构造
  17.     NoInt(int) = delete;    // 禁止从int构造
  18. };
复制代码
实际使用场景

这些关键字在以下场景非常有用:

  • 实现单例模式
  • RAII资源包装器
  • 禁止特定用法:如禁止在堆上创建对象
  1. // 禁止在堆上创建对象
  2. class NoHeapAllocation {
  3. public:
  4.     void* operator new(size_t) = delete;
  5.     void* operator new[](size_t) = delete;
  6. };
复制代码
override和final关键字

override关键字

override关键字用于显式标记覆盖基类的虚函数,资助制止常见错误:
  1. class Base {
  2. public:
  3.     virtual void foo() { std::cout << "Base::foo()" << std::endl; }
  4.     virtual void bar(int x) { std::cout << "Base::bar()" << std::endl; }
  5. };
  6. class Derived : public Base {
  7. public:
  8.     // 明确标记为覆盖,编译器会检查
  9.     void foo() override { std::cout << "Derived::foo()" << std::endl; }
  10.    
  11.     // 如果参数不匹配,编译器会报错
  12.     // void bar(double x) override { /* 错误:没有覆盖基类函数 */ }
  13. };
复制代码
final关键字

final关键字应用于类或虚函数,禁止进一步继承或覆盖:
  1. class Base {
  2. public:
  3.     virtual void foo() { std::cout << "Base::foo()" << std::endl; }
  4. };
  5. class Derived : public Base {
  6. public:
  7.     // 禁止进一步覆盖此函数
  8.     void foo() override final { std::cout << "Derived::foo()" << std::endl; }
  9. };
  10. // 定义为final的类不能被继承
  11. class FinalClass final {
  12.     // ...
  13. };
复制代码
强类型罗列

传统罗列的题目

C++98/03中的传统罗列存在作用域污染、类型不安全等题目:
  1. enum Color { RED, GREEN, BLUE };
  2. enum Fruit { APPLE, BANANA, ORANGE };
  3. // 问题:枚举值会泄漏到全局命名空间
  4. bool comparison = (RED == APPLE);  // 允许比较,都是0
复制代码
enum class的优势

C++11引入的强类型罗列办理了这些题目:
  1. enum class Color { RED, GREEN, BLUE };
  2. enum class Fruit { APPLE, BANANA, ORANGE };
  3. // 必须使用作用域操作符
  4. Color c = Color::RED;
  5. // 不同枚举类型不能直接比较
  6. // bool comparison = (Color::RED == Fruit::APPLE);  // 编译错误
  7. // 不会隐式转换为整数
  8. // int n = Color::GREEN;  // 编译错误
  9. int n = static_cast<int>(Color::GREEN);  // 正确,显式转换
复制代码
底层类型与前向声明

可以指定罗列的底层类型,并支持前向声明:
  1. // 指定底层类型
  2. enum class SmallEnum : uint8_t { A, B, C };
  3. // 前向声明
  4. enum class Status : int;  // 必须指定底层类型
复制代码
用户定义字面量

C++11允许为自定义类型创建字面量,通过定义特别的字面量运算符:
  1. // 距离字面量
  2. Distance operator"" _km(long double km) {
  3.     return Distance(km * 1000.0);  // 转换为米
  4. }
  5. // 使用
  6. auto marathon = 42.195_km;  // 创建Distance对象
复制代码
C++14尺度库添加了多个字面量:
  1. using namespace std::literals;
  2. // 字符串字面量
  3. auto str = "hello"s;  // std::string,不是C风格字符串
  4. // 时间字面量
  5. auto day = 24h;
  6. auto minute = 1min;
  7. // 复数字面量
  8. auto c = 3.0 + 4.0i;  // 复数(3, 4)
复制代码
noexcept阐明符

noexcept阐明符指定函数不会抛出异常:
  1. // 保证不抛出异常的函数
  2. void safeOperation() noexcept {
  3.     // 如果这里抛出异常,程序会立即终止
  4. }
  5. // 条件noexcept
  6. template<typename T>
  7. void swap(T& a, T& b) noexcept(noexcept(T(std::move(a)))) {
  8.     T temp = std::move(a);
  9.     a = std::move(b);
  10.     b = std::move(temp);
  11. }
复制代码
noexcept在移动语义中特别重要,尺度库容器使用它来决定是否使用移动操纵:
  1. // 移动构造函数标记为noexcept
  2. Widget(Widget&& other) noexcept
  3.     : name_(std::move(other.name_)),
  4.       data_(std::move(other.data_)) {
  5. }
复制代码
thread_local存储类型

thread_local指定变量的存储一连性为线程生命周期,每个线程有本身的副本:
  1. // 每个线程都有独立副本
  2. thread_local int counter = 0;
  3. void threadFunction() {
  4.     ++counter;  // 只影响当前线程的counter
  5.     std::cout << "Thread " << std::this_thread::get_id()
  6.               << ": " << counter << std::endl;
  7. }
复制代码
应用场景包括:

  • 线程安全的单例模式
  • 线程专用缓存
  • 无锁数据结构
内联命名空间

C++11引入了内联命名空间,简化版本管理和符号导出:
  1. namespace MyLib {
  2.     // v1命名空间内的符号直接可见
  3.     inline namespace v1 {
  4.         void func() { /* v1实现 */ }
  5.     }
  6.    
  7.     // v2命名空间需要显式访问
  8.     namespace v2 {
  9.         void func() { /* v2实现 */ }
  10.     }
  11. }
  12. // 直接使用v1版本
  13. MyLib::func();  // 调用MyLib::v1::func
  14. // 显式使用v2版本
  15. MyLib::v2::func();
复制代码
应用场景包括:

  • 库版本管理
  • 特性切换
  • 平台特定实现
总结

C++11/14引入的这些"其他语言特性"极大地提高了C++语言的表达本领和安全性:

  • nullptr:提供类型安全的空指针字面量
  • constexpr:允许编译期盘算表达式,增强性能和元编程本领
  • 范围for循环:简化容器迭代,提高代码可读性
  • 默认函数控制:通过default和delete精确控制特别成员函数
  • override和final:增强类继承体系的安全性和表达本领
  • 强类型罗列:提供更类型安全的罗列类型
  • 用户定义字面量:为自定义类型创建直观的字面量表现法
  • noexcept:明确函数的异常包管,优化移动语义
  • thread_local:提供线程专用存储,简化多线程编程
  • 内联命名空间:简化库版本管理和符号导出
掌握这些新特性,可以或许资助我们编写更加今世化、高效、安全的C++代码。它们固然各自看起来只是小改进,但共同提供了更强大、更具表达力的语言工具集。

这是我C++学习之旅系列的第四十四篇技术文章。检察完整系列目次了解更多内容。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表