C-特性和新特性

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;  
}
C++与Java

以下是对C++和Java更详细的比较,包括语法、跨平台性、性能、安全性以及各自的优势和应用场景等方面的详细分析:

一、语法和风格

C++

  • 面向对象:C++是一种支持面向对象的编程语言,但它也支持面向过程和泛型编程。
  • 复杂性:C++的语法相对复杂,包含指针、手动内存管理、多重继承等特性,这使得它在学习和使用时需要更多的注意和技巧。
  • 灵活性:由于C++提供了对底层硬件的直接访问能力,它允许开发者编写高度优化的代码,但同时也需要开发者对内存和资源的管理有深入的理解。

Java

  • 面向对象:Java是一种纯面向对象的编程语言,所有的代码都封装在类中。
  • 简洁性:Java的语法比C++更简单、更直观,易于学习和使用。它自动管理内存,减少了内存泄漏和指针错误的风险。
  • 平台无关性:Java的“一次编写,到处运行”特性使得它可以在任何支持Java虚拟机(JVM)的平台上运行,无需修改代码。
二、跨平台性

C++

  • 跨平台编译:C++可以通过编写可移植的代码或使用跨平台库来实现跨平台编译和运行,但这需要开发者对不同的操作系统有一定的了解,并编写相应的适配代码。
  • 平台依赖:在某些情况下,C++程序可能依赖于特定的操作系统或硬件特性,这限制了其跨平台能力。

Java

  • 自动跨平台:Java通过字节码和Java虚拟机(JVM)实现了真正的跨平台能力。Java程序在任何安装了JVM的平台上都可以运行,无需任何修改。
  • 广泛支持:由于JVM的广泛部署和支持,Java程序几乎可以在所有主流操作系统上运行。
三、性能

C++

  • 高性能:C++程序通常比Java程序具有更高的性能,因为它更接近底层硬件,且提供了更多的控制。
  • 编译型语言:C++是编译型语言,代码在编译时会被转换成机器码,这使得它可以在运行时直接执行,减少了运行时的开销。

Java

  • 优化性能:尽管Java在性能上不如C++,但通过即时编译器(JIT)和其他优化技术,Java程序的性能已经非常接近甚至在某些情况下超过C++。
  • 内存管理开销:Java的自动内存管理和垃圾回收机制虽然简化了编程过程,但也带来了一定的性能开销。
四、安全性

C++

  • 潜在风险:C++的复杂性使得它更容易出现安全问题,如缓冲区溢出、内存泄漏等。开发者需要更加小心地管理资源和避免常见的编程错误。
  • 直接硬件访问:C++的直接硬件访问能力在带来性能优势的同时,也增加了被恶意利用的风险。

Java

  • 较高安全性:Java通过其自动内存管理、强类型检查和运行时环境等特性提供了相对较高的安全性。
  • 沙箱模型:Java的沙箱模型限制了程序对系统资源的访问,进一步提高了安全性。
五、优势和应用场景

C++

  • 优势:高性能、直接硬件访问、丰富的库和生态系统、多范式编程支持。
  • 应用场景:系统级编程(如操作系统、驱动程序)、游戏开发、嵌入式系统、高频交易等。

Java

  • 优势:跨平台性、自动内存管理、丰富的类库和框架、强大的社区支持。
  • 应用场景:企业级应用(如ERP、CRM)、Web开发(如Servlet、JSP)、移动应用开发(如Android)、大数据处理(如Hadoop)等。
C++和Python

C++和Python是两种非常流行的编程语言,它们各自具有独特的特性和优势,适用于不同的编程场景和需求。以下是对C++和Python的详细比较和归纳:

C++
性能与效率
  • 编译型语言:C++是一种编译型语言,其代码被编译成机器码后直接在计算机上执行,因此执行速度通常比解释型语言快。
  • 高效性:C++编译生成的机器码效率高,可以直接与硬件进行交互,适合在性能要求较高的应用中使用。
底层控制
  • 内存管理:C++允许程序员直接管理内存,包括分配和释放内存,这提供了更大的灵活性和性能优势,但也增加了内存泄漏和悬挂指针的风险。
  • 指针操作:C++支持指针操作,允许直接访问内存地址,这在系统编程和嵌入式系统开发中非常有用。
面向对象编程
  • 支持面向对象编程:C++支持面向对象编程,允许使用类和对象来组织代码,提高了代码的可重用性和可维护性。
  • 多范式编程:除了面向对象编程外,C++还支持泛型编程和函数式编程等不同的编程范式。
标准库与移植性
  • 丰富的标准库:C++标准库提供了丰富的数据结构和算法,帮助开发者更容易地实现各种功能。
  • 良好的移植性:C++编写的程序可以在多个平台上编译和运行,具有很好的移植性。
复杂性与学习曲线
  • 相对复杂:C++的语法相对较为复杂,需要程序员更加关注内存管理和指针操作,因此学习曲线较陡。
  • 开发速度:由于需要手动管理内存和进行更多的底层操作,相比Python等高级语言,C++的开发速度通常较慢。
Python
简洁易读
  • 简洁的语法:Python以其简洁优美的语法而闻名,代码量通常更少,更易于阅读和理解。
  • 动态类型:Python是一种动态类型语言,在运行时可以根据赋值来自动推断变量的类型,增加了代码的灵活性。
高级特性
  • 自动内存管理:Python使用垃圾回收机制自动管理内存,开发者无需操心内存释放的问题,这使得Python编程更加安全和简单。
  • 支持多种编程范式:Python支持面向对象编程、函数式编程等高级特性,提供了更高的开发效率。
丰富的库与生态系统
  • 庞大的标准库和第三方库:Python拥有庞大且活跃的社区,提供了丰富的第三方库和工具,使得开发者能够快速解决各种问题。
  • 广泛的应用领域:Python广泛用于Web开发、数据分析、人工智能、科学计算等领域,是人工智能领域的主要编程语言之一。
开发速度与易用性
  • 易学易用:Python的语法简单清晰,易于学习和阅读,适合初学者入门编程。
  • 快速开发:Python的简洁性和丰富的库使得开发者能够快速编写和测试代码,提高开发效率。
  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值