首页 > 其他分享 >C-特性和新特性

C-特性和新特性

时间:2024-07-02 13:00:44浏览次数:3  
标签:std int 特性 线程 C++ ptr 表达式

C++特性和新特性

C++11

C++11是C++编程语言的一个重要标准版本,是C++98标准发布后13年来的第一次重大修正,它引入了许多新特性和改进,极大地增强了C++语言的表达能力和开发效率。

C++11是C++编程语言的一个重要标准版本,由国际标准化组织(ISO)和国际电工委员会(IEC)旗下的C++标准委员会(ISO/IEC JTC1/SC22/WG21)于2011年8月12日公布,并于2011年9月正式出版,其标准文件号为ISO/IEC 14882:2011。C++11是C++98标准发布后13年来的第一次重大修正,它引入了许多新特性和改进,极大地增强了C++语言的表达能力和开发效率。以下是对C++11的一些主要特性和改进的归纳:

核心语言的新机能
自动类型推导(auto)

C++11引入了auto关键字,用于自动推导变量的类型,使得代码更加简洁,特别是在处理复杂类型或模板类型时。

#include <iostream>  
#include <vector>  
  
int main() {  
    // 使用auto推导vector<int>类型  
    auto myVector = std::vector<int>{1, 2, 3, 4, 5};  
  
    // 遍历vector  
    for(auto it = myVector.begin(); it != myVector.end(); ++it) {  
        std::cout << *it << std::endl;  
    }  
  
    // 推导函数返回类型  
    auto result = myVector.size(); // result的类型是std::vector<int>::size_type,通常是unsigned int  
  
    return 0;  
}
decltype

与auto类似,但decltype用于推导表达式的类型,适用于auto无法使用的场景。

#include <iostream>  
  
int main() {  
    int x = 5;  
    // 使用decltype获取x的类型,并声明同类型的变量y  
    decltype(x) y = x;  
  
    // 也可以用于复杂表达式  
    int a = 1, b = 2;  
    decltype(a + b) c = a + b; // c的类型是int  
  
    // 引用类型  
    int& ref = x;  
    decltype(ref) anotherRef = y; // anotherRef也是int&类型  
  
    // 注意,decltype(表达式)的结果取决于表达式的形式  
    decltype((x)) wholeX = x; // wholeX是int&,因为表达式(x)是左值  
  
    return 0;  
}
右值引用和移动语义

引入了右值引用(T&&)和移动语义,允许临时对象(右值)的资源被“窃取”以进行高效的资源转移,避免了不必要的拷贝操作。

  • 右值引用:右值引用是C++11中引入的一个关键特性,它允许程序员显式地将一个表达式标记为右值,从而可以利用移动语义进行优化。在C++中,每个表达式都可以被分类为左值或右值。左值是指那些可以取地址的表达式,如变量、数组元素等,而右值则是指那些不能取地址的表达式,如字面量、临时变量、表达式求值结果等。右值引用就是用来引用这些右值的类型,其语法是在变量名前添加两个连续的“&”符号,如“int&&”。
int&& rvalueRef = 10; // 正确:10是右值,可以被右值引用绑定  
int x = 10;  
int&& rvalueRef2 = std::move(x); // 正确:使用std::move将x转换为右值
  • 移动语义:移动语义是C++11中引入的一种新的语言特性,旨在提高程序的性能和资源管理效率。其核心概念在于允许对象间资源的转移,而非传统的拷贝操作。移动语义通过右值引用和移动构造函数(以及移动赋值运算符)实现了资源的所有权从一个对象到另一个对象的转移,从而避免了不必要的复制操作。
  • 移动构造函数:接收一个右值引用参数,并将其资源“移动”到新的对象中,而不是复制这些资源。移动构造函数的定义形式为ClassName(ClassName&& other);
  • 移动赋值函数:用于将一个对象的资源转移给另一个已经存在的对象,其定义形式通常为ClassName& operator=(ClassName&& other);
class MyClass {  
public:  
    MyClass(int* data) : ptr(data) {}  
    MyClass(MyClass&& other) noexcept : ptr(other.ptr) {  
        other.ptr = nullptr; // 确保原对象不再拥有资源  
    }  
    MyClass& operator=(MyClass&& other) noexcept {  
        if (this != &other) {  
            delete[] ptr; // 释放原资源  
            ptr = other.ptr;  
            other.ptr = nullptr; // 确保原对象不再拥有资源  
        }  
        return *this;  
    }  
    ~MyClass() { delete[] ptr; }  
  
private:  
    int* ptr;  
};  
  
MyClass createMyClass() {  
    return MyClass(new int[100]); // 返回临时对象,将触发移动语义  
}  
  
MyClass obj = createMyClass(); // 这里将调用移动构造函数
统一的初始化

C++11引入了统一的初始化语法({}),使得所有类型的对象都可以使用相同的初始化方式。通过大括号{}std::initializer_list提供了更加灵活、安全、直观的初始化方法。

  1. 使用大括号{}直接初始化
    • 对于基本数据类型:int a{10};
    • 对于数组:int arr[3]{1, 2, 3}; 或者 int arr[3] = {1, 2, 3};(注意,这里=并不是传统意义上的拷贝初始化,而是C++允许的一种简写形式,仍然属于统一初始化)
    • 对于结构体和类对象:struct Point{int x, y;} p{1, 2};
    • 对于容器:std::vector<int> vec{1, 2, 3};
  2. 使用std::initializer_list
    • 在C++11中,许多容器(如std::vectorstd::map等)都增加了接受std::initializer_list作为参数的构造函数,使得容器初始化更加方便。
    • std::initializer_list是一个轻量级的、可以容纳固定数量元素的容器,它在初始化时自动生成,并在初始化结束后销毁。
Lambda表达式

提供了一种定义匿名函数对象的方式,使得编写回调函数等更加简洁方便。Lambda 表达式特别适用于需要函数对象但又不想正式命名一个函数的场景,比如作为算法(如 std::sort)的参数,或者在需要回调函数的地方。

[capture](parameters) mutable -> return_type {  
    // 函数体  
}
  • 捕获列表[capture]):指定哪些外部变量在 lambda 表达式内部是可见的。如果省略捕获列表,则 lambda 表达式不能访问任何外部变量。捕获列表可以是值捕获(通过拷贝)或引用捕获(通过引用)。
  • 参数列表(parameters)):与普通函数的参数列表相同,定义了 lambda 表达式的参数。如果 lambda 表达式不接受任何参数,则可以省略参数列表。
  • mutable 关键字(可选):允许在 lambda 表达式体内修改被捕获的变量的值(如果它们是通过值捕获的)。默认情况下,这些变量在 lambda 表达式内是不可变的。
  • 返回类型-> return_type):指定 lambda 表达式的返回类型。如果 lambda 表达式体只包含一个返回语句,并且编译器可以推导出返回类型,则可以省略返回类型。
  • 函数体:定义了 lambda 表达式的操作。
  • 一个计算两数之和的简单例子:
#include <iostream>  
#include <algorithm>  
#include <vector>  
  
int main() {  
    int a = 10, b = 20;  
  
    // 定义一个 lambda 表达式,计算两个整数的和  
    auto sum = [](int x, int y) { return x + y; };  
  
    // 使用 lambda 表达式  
    std::cout << "The sum is " << sum(a, b) << std::endl;  
  
    // 使用 lambda 表达式作为 std::sort 的比较函数  
    std::vector<int> vec = {4, 1, 3, 5, 2};  
    std::sort(vec.begin(), vec.end(), [](int x, int y) { return x < y; });  
  
    for (int n : vec) {  
        std::cout << n << ' ';  
    }  
    std::cout << std::endl;  
  
    return 0;  
}
标准库的扩展
智能指针

C++11标准库增加了shared_ptr、unique_ptr和weak_ptr等智能指针,用于自动管理动态分配的内存,减少内存泄漏的风险。

  1. unique_ptr
    • 独占式智能指针,确保只有一个指针可以指向资源。
    • 通过std::move()函数可以转移资源的所有权。
#include <iostream>  
#include <memory>  
  
class MyClass {  
public:  
    MyClass(int value) : value_(value) {}  
    void print() const { std::cout << "Value: " << value_ << std::endl; }  
  
private:  
    int value_;  
};  
  
int main() {  
    // 创建一个 unique_ptr 指向 MyClass 的实例  
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(10);  
  
    // 使用 ptr  
    ptr->print();  
  
    // 当 ptr 离开作用域时,它指向的 MyClass 实例将被自动销毁  
  
    return 0;  
}
  1. shared_ptr
    • 共享式智能指针,允许多个指针共享同一个资源。
    • 采用引用计数机制,当所有shared_ptr对象都不再需要该资源时,资源会自动被销毁。
//Myclass同上
int main() {  
    // 创建两个 shared_ptr 指向同一个 MyClass 实例  
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(20);  
    std::shared_ptr<MyClass> ptr2 = ptr1; // ptr2 和 ptr1 共享所有权  
  
    // 使用 ptr1 和 ptr2  
    ptr1->print();  
    ptr2->print();  
  
    // 当 ptr1 和 ptr2 都离开作用域时,MyClass 实例将被销毁  
  
    return 0;  
}
  1. weak_ptr
    • 弱引用智能指针,用于辅助shared_ptr工作,不增加资源的引用计数。
    • 当所有的shared_ptr对象都不再需要该资源时,weak_ptr对象会自动失效。
//Myclass同上
int main() {  
    // 创建一个 shared_ptr 和一个 weak_ptr  
    std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(30);  
    std::weak_ptr<MyClass> weakPtr = ptr;  
  
    // 使用 ptr  
    ptr->print();  
  
    // 尝试通过 weakPtr 访问资源,需要先锁定 weakPtr  
    if (auto lockedPtr = weakPtr.lock()) {  
        lockedPtr->print();  
    } else {  
        std::cout << "weak_ptr is expired!" << std::endl;  
    }  
  
    // 当 ptr 离开作用域时,MyClass 实例将被销毁  
    // weakPtr 将自动变为过期状态  
  
    return 0;  
}
无序容器

新增了unordered_map和unordered_set等基于哈希表的容器,提供了比原有map和set更高的查找效率。点击了解更多容器知识:STL标准模板库容器操作集合-CSDN博客

  1. std::unordered_map:存储键值对,其中每个键都是唯一的。键和值可以是任何可复制且可赋值的类型。
  2. std::unordered_multimap:与unordered_map类似,但允许键重复。
  3. std::unordered_set:只存储键的集合,键是唯一的。
  4. std::unordered_multiset:与unordered_set类似,但允许键重复。
正则表达式

标准库增加了对正则表达式的支持,使得字符串处理更加灵活和强大。从C++11开始,C++标准库引入了<regex>头文件,提供了对正则表达式的支持。C++中的正则表达式类和相关函数主要包括:

  1. std::regex:定义包含正则表达式的对象。
  2. std::smatchstd::cmatch:定义保存匹配结果的对象,分别用于string类型和char*类型的字符串。
  3. 常用正则匹配函数
    • std::regex_match:判断整个目标字符串是否与正则表达式完全匹配。
    • std::regex_search:在目标字符串中搜索与正则表达式匹配的第一个子字符串。
    • std::regex_replace:用指定的字符串替换与正则表达式匹配的部分。
#include <iostream>  
#include <regex>  
#include <string>  
  
int main() {  
    std::string s = "hello, world!";  
    std::regex e("\\bhello\\b"); // 匹配单词"hello"  
  
    if (std::regex_search(s, e)) {  
        std::cout << "Match found!" << std::endl;  
    } else {  
        std::cout << "No match." << std::endl;  
    }  
  
    // 替换匹配到的内容  
    std::string replaced = std::regex_replace(s, e, "Hi");  
    std::cout << replaced << std::endl; // 输出: "Hi, world!"  
  
    return 0;  
}
线程支持

C++11首次在标准库中引入了线程支持,包括线程(std::thread)、互斥锁(std::mutex)、条件变量(std::condition_variable)等,使得C++能够更方便地进行多线程编程。以下是C++11中线程支持的主要特点:

  1. std::thread类
    • std::thread是C++11中用于表示线程的类。通过创建std::thread的实例并传递给它一个可调用对象(如函数、lambda表达式、函数对象等),可以启动一个新的线程来执行该可调用对象。
    • std::thread的构造函数有多种重载形式,允许传递不同数量和类型的参数给线程函数。
  2. 线程控制
    • join():等待线程结束。调用线程(通常是主线程)会阻塞,直到被join的线程执行完毕。join操作是线程同步的一种方式。
    • detach():分离线程,使其独立于主线程运行。一旦线程被分离,就不能再对其执行join操作。分离后的线程在结束时会自动释放资源。
    • joinable():检查线程是否可以被join。如果线程已经被join或detach,或者线程对象从未与任何执行线程关联,则joinable()返回false。
  3. 线程ID
    • 每个std::thread对象都有一个唯一的标识符,可以通过调用get_id()成员函数来获取。这个ID可以用于区分不同的线程。
  4. 线程互斥与同步
    • C++11还引入了<mutex><condition_variable>等头文件,提供了互斥锁、条件变量等同步机制,用于解决多线程中的数据竞争和同步问题。
#include <iostream>  
#include <thread>  
 //在这个示例中,我们创建了两个线程t1和t2,它们分别执行threadFunction函数,并传递不同的参数。然后,主线程通过调用join()函数等待这两个线程结束。
void threadFunction(int n) {  
    for (int i = 0; i < 5; ++i) {  
        std::cout << "Thread: " << n << ", Count: " << i << std::endl;  
    }  
}  
  
int main() {  
    std::thread t1(threadFunction, 1);  
    std::thread t2(threadFunction, 2);  
  
    // 等待两个线程结束  
    t1.join();  
    t2.join();  
  
    return 0;  
}
其他重要特性
nullptr

引入了nullptr作为空指针的字面量,替代了原来的NULL宏,提高了代码的安全性和可读性。

  • nullptr 是一个特殊的关键字,其类型是 std::nullptr_t。这个类型只能被隐式转换为指针类型,而不能被转换为整数类型,从而避免了类型不匹配的问题。
  • 相比之下,NULL 通常被定义为 0((void*)0),可以隐式地转换为任何指针类型或整数类型,这可能导致意外的类型转换错误。
基于范围的for循环

提供了一种更简洁的遍历容器或数组的方式,使得代码更加简洁易读。

for (declaration : expression) {  
    // 循环体  
}
  1. 元素类型:在declaration中声明的类型应该与容器中元素的类型相匹配,或者至少是容器中元素类型的可隐式转换类型。
  2. 修改元素:如果你需要在循环中修改元素的值,并且这个修改对容器是可见的,你应该使用元素的引用(通过&)来声明变量。例如:
for (int& num : nums) {  
    num *= 2; // 将会修改容器中的元素  
}

例子

#include <iostream>  
#include <vector>  
  
int main() {  
    std::vector<int> nums = {1, 2, 3, 4, 5};  
  
    // 使用基于范围的for循环遍历vector  
    for (int num : nums) {  
        std::cout << num << " ";  
    }  
  
    return 0;  
}
变长参数模板

允许模板参数的数量在编译时确定,为泛型编程提供了更强大的能力。(没看懂,以后再说)

constexpr

允许在编译时计算表达式的值,并用于常量表达式的定义,提高了程序的运行效率。使用 constexpr 可以提高程序的性能,因为它允许编译器在编译时进行更多的优化,而不是在运行时计算表达式的值。

  • 变量:当用于变量时,constexpr 变量必须在声明时初始化,并且其值必须是编译时常量。这意味着它不能依赖于运行时才能确定的值,如用户输入或文件读取。
constexpr int max_value = 100; // 正确:编译时常量  
// constexpr int x = get_value_from_user(); // 错误:不是编译时常量  
  
// 可以在编译时计算  
constexpr int square(int x) {  
    return x * x;  
}  
  
constexpr int result = square(5); // 正确:result 的值是 25,在编译时确定
  • 函数:当用于函数时,constexpr 函数表示该函数可以在编译时求值,但并非所有 constexpr 函数都必须在编译时调用。如果一个 constexpr 函数在编译时没有被用于需要常量表达式的上下文中,它也可以像普通函数一样在运行时被调用。
constexpr int factorial(int n) {  
    return n <= 1 ? 1 : n * factorial(n - 1);  
}  
  
int main() {  
    constexpr int value = factorial(5); // 在编译时计算  
    std::cout << "Factorial of 5 is " << factorial(10) << std::endl; // 在运行时计算  
    return 0;  
}

标签:std,int,特性,线程,C++,ptr,表达式
From: https://blog.csdn.net/longer_net/article/details/140123550

相关文章

  • TypeScript一些特性让代码更优雅
    TypeScript不仅仅是JavaScript的类型超集,它还提供了一系列强大的高级特性,可以显著提高代码的质量和可维护性,掌握TypeScript的这些高级功能,不仅可以让你的代码更加健壮,还能大大提升你的开发效率。赶紧来看看吧!一、深入理解TypeScript的高级类型推断TypeScript的类型推断系......
  • 学懂C#编程:常用高级技术——学会C#的高级特性 反射
    反射(Reflection)是C#中的一项高级特性,它允许程序在运行时检查和操作程序集、模块、类型等元数据信息。通过反射,你可以在不知道类型信息的情况下,动态地创建对象、调用方法、访问属性等。反射的基本概念反射的核心在于System.Reflection命名空间,它提供了许多类和接口,用于获取类......
  • C++11新特性
    1.字符串原始字面量        在C++11中添加了定义原始字符串的字面量,定义方式为:R“xxx(原始字符串)xxx”其中()两边的字符串可以省略。原始字面量R可以直接表示字符串的实际含义,而不需要额外对字符串做转义或连接等操作。        比如:编程过程中,使用的字符串中......
  • 说一说ABAP CDS View的发展历史与特性
    1.背景随着SAPFiori应用程序的兴起,SAP领域的小伙伴接触和使用ABAPCDSView的机会也是越来越多。今天,让我们花些时间,一起在了解下这项技术的设计初衷和发展历史。2.设计初衷说起ABAPCDSView,就不得不提及SAPHANA。SAPHANA引入了内存计算技术,这让ABAP开发范式发生了......
  • PHP的最新版本是什么?它引入了哪些新特性?
    PHP(全称:PHP:HypertextPreprocessor,即"PHP:超文本预处理器")是一种通用开源脚本语言,主要用于服务器端编程。它在服务器上执行,能够生成动态页面内容、处理表单数据、发送和接收Cookies、操作数据库等。PHP语法借鉴了C语言,并吸纳了Java和Perl的特色,发展出自己的独特语法。此外,PHP也......
  • C++ 20新特性之改进的位操作
    ......
  • 常用JS特性浏览器支持版本查询
    此文仅供自己快速查询常用的特性。红色表示不支持。FeatureiOSAndroidChromeFirefoxES6Class10.34.4.44946箭头函数104.4.44522let10(for循环scope错误),114.4.441(strict模式),4944const10(scope错误),114.4.4(非严格模式)41(非严格没块域)......
  • 特性:定义共享行为
    特性:定义共享行为特性定义了特定类型具有并可以与其他类型共享的功能。我们可以使用特性以抽象的方式定义共享行为。我们可以使用特性边界来指定泛型类型可以是任何具有特定行为的类型。注意:特性类似于其他语言中通常称为接口的功能,尽管存在一些差异。定义特性一个类型的行为......
  • Vue3的Composition API:Composition API是Vue3的一大新特性,它提供了一种更灵活的方式来
    1.介绍1.什么是CompositionAPI CompositionAPI是Vue.js3.0中引入的一项新特性,提供了一种新的、更灵活的方式来组织Vue组件的代码。CompositionAPI是基于函数的,并允许在组件的setup函数中直接处理响应式数据和生命周期钩子,使得代码更加清晰,更便于维护和测......
  • Memcached分布式特性解析:高效缓存策略的关键
    在现代的互联网应用中,缓存是提高性能和扩展性的关键技术之一。Memcached作为一个高性能的分布式内存缓存系统,广泛用于减轻数据库负载、加快数据访问速度。本文将深入探讨Memcached的分布式特性,包括其工作原理、集群管理、数据一致性、故障恢复以及与其他分布式系统的集成等......