C++发展历史C++98和C++11详细介绍

一、C++语言发展介绍

        设计C++是为了解决一个问题:如何直接操作硬件,同时又支持高效、高级的抽象。、

        组成部分:语言、标准库、许多其它的库、庞大的旧代码、工具

        C++开始于1979年4月,一个带类的C。

        1979年:new和delete函数

        1981年:const-支持接口和符号常量的不变性

        1982年:虚函数-提供运行期多态

        1984年:引用-支持运算符重载和简化的参数传递

                运算符和函数重载-除了算法和逻辑运算符外,还包括:赋值=、调用()、下表[]和智能指针->。

        1987年:类型安全连接-消除许多来自不同翻译单元中不一致的声明错误

                抽象类-提供纯接口。

        1980:

        模板-经过多年使用宏进行泛型编程之后,模板更好的支持泛型编程

        异常-试图给混乱的错误带来某种秩序

二、C++语言总结

        更好的C

        支持数据抽象

        支持面向对象编程

        支持泛型编程

三、C++98

        模板:无约束、图灵完备、对泛型编程的编译器支持

        异常:一套在单独路径上返回错误值的机制,由调用方栈顶上的“在别处”的代码处理

        namespace:命名空间。允许程序员在编写独立部分组成的较大程序时,避免名称的冲突

        条件语句的声明:写法紧凑、限制变量作用域

       具名 类型转换:static_cast、

        bool:布尔类型,曾经使用int作为布尔变量和常量

一个文件打开操作
{
    FILE* P = fopen(name,"r");
    fclose(p);
}

        文件句柄的泄露会比内存泄漏更快的耗尽 操作系统的资源

C++98标准库提供了:

        STL:创造性的、通用的、优雅的、高效的容器、迭代器 和算法框架

        特征 (trait):对使用模板编程有用的编译器属性集

        string:保存和操作字符序列的类型。字符类型是一个模板参数,默认值为char

        iostream:数据流。处理字符类型、区域设置、缓冲区策略

        bitset:保存和操作比特位集合的类型

        locale:处理不同文化传统的精致框架,主要与输入输出有关

        valarray:一个数值数组。很少被使用

        auto_ptr:早期代表独占所有权的指针;在C++11中被shared_ptr和unique_ptr替代

四、C++11

        C++11引入了大量令人眼花缭乱的东西

        内存模型:一个高效的现代硬件设计的底层抽象,作为描述并非的基础

        auto和decltype:避免类型名称和不必要的冲突

        范围for:对范围的简单顺序遍历

        移动语义和右值引用:目的是减少数据拷贝

        统一初始化:初始化语法

        nullptr:给空指针一个名字

        constexpr:在编译器进行求值的函数

        用户定义字面常量:为用户自定义的类型,提供字面常量

        原始字符串字面量:不需要转义字符的字面量,主要用在正则表达式中

        属性:将任意信息同一个名字关联。

        lambda表达式:匿名函数

        变参模板:可以处理任意个任意类型的参数的模板

        模板别名:能够重命名模板并为新名称绑定一些模板参数

        noexcept:确保函数不会抛出异常,提高效率

        override和final:管理大型类层次结构的语法

        static_assert:编译器断言

        long long:更长的整数类型

        默认成员初始化器:给数据成员一个默认值,这个默认值可以被构造函数中的初始化覆盖

        enum class:枚举类型定义带有作用域的强制类型枚举

标准库组件

        unique_ptr和shared_ptr依赖RAII的资源管理指针

        内存模型和atomic变量

        thread、mutex、condition_variable等为基本的系统层级的并发提供了类型安全、可移植的支持。

        future、promise和packaged_task稍稍高级的并发

        tuple匿名的简单复合类型

        类型特征-类型的可测试属性,用于元编程

        正则表达式匹配

        随机数-带有许多生成器和多种分布

        时间-time_point和duration

        unordered_map哈希表

        forward_list单向链表

        array具有固定常量大小的数组,并且会记住自己的大小

        emplace运算-在容器内直接构造对象,避免拷贝

        exception_ptr允许在线程之间传递异常

        一些看似不连贯的语法规则,互不相关的扩展怎么能组成一个连贯的整体呢。当你使用的多了,融入到你的日常编码中,会发现他们节省了开发时间。

总结

        支持并发

        简化使用

        改进对泛型编程的支持

        增加静态类型安全

        支持对库的开发

        标准库组件

五、C++11:并发支持

内存模型
        atomic类型
atomic<int> x;
int n;

void main()
{

    x++;
    n++;
}
        mutex
mutex mt;
atomic<bool> b;
atomic<int> x;

if(b)
{
    lock_guard<mutex> lock(mt);
    if(b)
    {
        x++;
    }

} // 自动释放mt锁

        lock_guard 是一种RAII类型,确保会解锁它所控制的mutex。

        锁的开销比atomic大的多的多。

        无锁编程才是专家级别的工作

        

六、线程和锁

        thread:系统的执行线程,支持join()和detach()

        mutex:系统的互斥锁,支持lock()和unlock(),要保证unlock()

        condition_variable:系统中线程间进行事件通信的条件变量

        thread_local:线程本地存储

#include <iostream>
#include <thread>

void printMessage(const std::string& message) {
    std::cout << message << std::endl;
}

int main() {
    std::thread t(printMessage, "Hello from thread with a message!");
    t.join(); // 等待线程 t 完成
    return 0;
}
        匿名函数
#include <iostream>
#include <thread>

int main() {
    std::thread t([]() {
        std::cout << "Hello from a lambda thread!" << std::endl;
    });
    t.join(); // 等待线程 t 完成
    return 0;
}

join():等待线程结束。在调用 join() 之前,主线程会阻塞,直到线程完成。

detach():允许线程独立运行。调用 detach() 后,线程会与主线程分离,继续在后台运行。

#include <iostream>
#include <thread>

void longRunningTask() {
    // 执行一些长时间运行的任务
}

int main() {
    std::thread t(longRunningTask);
    t.detach(); // 线程 t 现在在后台运行
    // 主线程会继续执行,而 t 线程在后台运行
    return 0;
}
线程安全
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedData = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    ++sharedData;
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << "Shared data: " << sharedData << std::endl;
    return 0;
}
注意事项:
  • 使用 std::thread 时,确保在线程结束前调用 join() 或 detach(),否则程序可能无法正确退出。
  • 避免在线程销毁前没有调用 join() 或 detach(),这会导致未定义行为。
  • 使用 std::lock_guard 或 std::unique_lock 管理互斥锁,以确保线程安全。

七、最后C++11还提供多线程async()函数

        async()函数,可以启动一个任务并在另一个thread上执行

        future一个句柄,通过它可以从一个共享的单对象缓冲区中get()一个值,可能需要等待某个promise将该值放入缓冲区

        promise一个句柄,通过它可以将一个值put()到一个共享的单对象缓冲区,可能会唤醒某个等待future的thread

        packaged_task一个类,它使得设置一个函数在线程上异步执行变得容易,由future来接受promise返回结果

        

#include <future>
#include <iostream>

int asyncFunction() {
    // 执行一些计算密集型任务
    return 520; // 返回结果
}

int main() {
    // 启动异步任务
    std::future<int> result = std::async(std::launch::async, asyncFunction);
    
    // 在此处可以执行其他任务...

    // 获取异步任务的结果
    int value = result.get(); // 阻塞直到异步任务完成
    std::cout << "The answer is " << value << std::endl;

    return 0;
}

          

处理异步任务的结果

std::future::get() 方法会阻塞调用线程,直到异步任务完成,并返回任务的结果。

错误处理

如果异步任务抛出异常,std::future::get() 会重新抛出该异常。

注意事项:
  • std::async 有两个参数:第一个是启动策略,第二个是要执行的任务。std::launch::async 确保任务在新线程中异步执行。
  • 如果不指定启动策略,std::async 可能会立即同步执行任务,而不是异步。
  • std::future 对象在默认情况下是移动语义的,可以被移动,但不能被复制。
  • std::future::get() 只能调用一次,因为调用后它会消费掉 future 对象中的结果。

        

八、C++11举例

1、auto关键字

std::string str1 = "Hello";
std::string str2 = "World";

auto s = str1 +str2;


std::vecto<int> vec;

auto itor = vec.begin();

2、范围for

vector<int> vec;

for(auto itor : vec)
{
    cout << itor <<endl;
}


int sum = 0;
for(auto i : {1,2,3,4,5,6,7,8,9})
{
    sum++i;
}

3、移动语义

        C++11 引入了移动语义,这是一种语言特性,它允许资源(如动态分配的内存、文件描述符、网络连接等)在不同对象之间高效地转移,而无需显式地复制。移动语义的主要目的是减少不必要的对象复制,从而提高程序的性能和效率。

  1. 移动构造函数

    • 移动构造函数是一种特殊的构造函数,它接受一个将要被移动的对象的引用(通常是右值引用)作为参数,并将其资源“移动”到新对象中。
  2. 移动赋值运算符

    • 移动赋值运算符允许将一个对象的资源移动到另一个对象中,而不是复制。
  3. 右值引用

    • 右值引用使用 && 符号声明,它允许程序员明确地表示一个对象是临时的,应该被移动而不是被复制。
  4. std::move

    • std::move 是一个标准库函数,它将一个对象转换为右值引用,从而允许移动构造函数或移动赋值运算符被调用。
#include <iostream>
#include <vector>

class MyClass {
public:
    std::vector<int> data;

    // 移动构造函数
    MyClass(MyClass&& other) : data(std::move(other.data)) {
        std::cout << "Move constructor called" << std::endl;
    }

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) {
        if (this != &other) {
            data = std::move(other.data);
            std::cout << "Move assignment called" << std::endl;
        }
        return *this;
    }
};

int main() {
    MyClass a;
    a.data.push_back(1);
    MyClass b = std::move(a); // 调用移动构造函数

    MyClass c;
    c = std::move(b); // 调用移动赋值运算符

    return 0;
}

    MyClass 包含了一个 std::vector<int> 成员。移动构造函数和移动赋值运算符使用 std::move 来获取 data 成员的所有权,而不是复制它。这使得 bc 能够高效地接收 a 的资源。

4、智能指针

        shared_ptr:代表共享所有权

        unique_ptr:代表独占所有权

5、统一初始化

        统一初始化使用花括号 {} 包围初始化值,并且可以用于所有类型的初始化,包括基本数据类型、类对象、数组和聚合类型。

        

#include <iostream>
#include <vector>

class MyClass {
public:
    int a;
    double b;
    std::string c;

    MyClass(int a, double b, std::string c) : a(a), b(b), c(c) {}
};

int main() {
    int n; // 默认初始化
    int n1 = 520; // 值初始化
        




    int x{10}; // 统一初始化
    double y = {20.0}; // 统一初始化,也可以省略等号
    std::vector<int> v{1, 2, 3, 4, 5}; // 统一初始化
    MyClass obj{100, 200.5, "Hello"}; // 统一初始化,并且调用 MyClass 的构造函数

    return 0;
}

6、nullptr 空指针

        

int *p = nullptr;

7、constexpr关键字

        用于声明常量表达式。constexpr 的目的是在编译时计算表达式的值,而不是在运行时,这样可以提高程序的效率,并且使得一些在编译时才能确定的值能够用于定义常量。

constexpr 的特点:
  1. 编译时计算constexpr 修饰的变量或函数必须在编译时就能确定其值,这样才能保证在编译时进行计算。

  2. 常量表达式constexpr 修饰的变量必须是一个常量表达式,即它们的值在编译时就能确定,且不会改变。

  3. 模板参数constexpr 可以用于模板参数,使得模板能够根据编译时已知的值进行实例化。

  4. 内联函数constexpr 函数在调用时会像内联函数一样,将其代码直接替换到调用点。

constexpr 的应用:
  1. 定义常量

    constexpr int maxConnections = 10;
  2. 定义复杂常量表达式

    constexpr int factorial(int n) {
        return n <= 1 ? 1 : n * factorial(n - 1);
    }
  3. 用于模板参数

    template <constexpr int N>
    void printFactorial() {
        std::cout << "Factorial of " << N << " is " << factorial(N) << std::endl;
    }
  4. 定义constexpr函数

    constexpr double square(double x) {
        return x * x;
    }
constexpr 的限制:
  1. 运行时不可用constexpr 函数不能包含运行时才能确定的表达式,如调用非constexpr函数。

  2. 不能抛出异常constexpr 函数不能包含可能抛出异常的代码。

  3. 不能有多个定义constexpr 函数只能有一个定义,通常在头文件中定义。

示例:
#include <iostream>

constexpr int add(int a, int b) {
    return a + b;
}

constexpr int multiply(int a, int b) {
    return a * b;
}

int main() {
    constexpr int sum = add(1, 2);
    constexpr int product = multiply(3, 4);

    std::cout << "Sum: " << sum << std::endl;
    std::cout << "Product: " << product << std::endl;

    return 0;
}

    addmultiply 函数都被声明为 constexpr,这意味着它们必须在编译时就能计算出结果。在 main 函数中,sumproduct 变量也是在编译时就确定了值。

8、别名

        

typedef int (*pf)(int); // pf是一个函数指针

9、tuple元组

        存储不同类型的数据项

#include <iostream>
#include <tuple>

int main() {
    // 创建一个包含 int, double, std::string 的 tuple
    std::tuple<int, double, std::string> myTuple = {10, 3.14, "Hello"};

    // 访问 tuple 中的元素
    std::cout << "Int: " << std::get<0>(myTuple) << std::endl;
    std::cout << "Double: " << std::get<1>(myTuple) << std::endl;
    std::cout << "String: " << std::get<2>(myTuple) << std::endl;

    // 使用 std::tie 来解包 tuple
    int i;
    double d;
    std::string s;
    std::tie(i, d, s) = myTuple;
    std::cout << "Using std::tie: " << i << ", " << d << ", " << s << std::endl;

    // 使用 std::apply 来调用函数
    std::cout << "Sum: " << std::apply([](int x, double y, const std::string& z) {
        return x + y + z.length();
    }, myTuple) << std::endl;

    return 0;
}

10、enum class

        enum classenum struct语法,这被称为强类型枚举或枚举类。这种新的枚举定义方式提供了更好的类型安全,并且可以避免枚举值在不同枚举类型之间的隐式转换。

        

在 C++11 之前,枚举是这样定义的:

enum Color { RED, GREEN, BLUE };

        这种定义方式存在一些问题,例如枚举值的类型不明确,它们默认会被提升为整型,并且枚举值可以隐式转换为整数,这可能导致类型安全问题。

C++11 枚举类定义:

C++11 引入了枚举类,使用 enum classenum struct 关键字:

enum class Color { RED, GREEN, BLUE };

或者:

enum struct Color { RED, GREEN, BLUE };

这两种方式是等价的,枚举类提供了以下优势:

  1. 类型安全:枚举类的成员不能隐式转换为整数类型,这避免了与整数的意外混用。

  2. 命名空间隔离:枚举类的成员不会污染全局命名空间,它们的访问需要通过枚举类型名作为前缀。

  3. 显式转换:如果需要将枚举值转换为整数,可以显式地进行转换。

#include <iostream>

enum class Color { RED, GREEN, BLUE };

int main() {
    Color myColor = Color::RED;
    
    // 需要显式转换为整数
    int redAsInt = static_cast<int>(myColor);

    std::cout << "My color is " << redAsInt << std::endl;

    // 比较枚举值
    if (myColor == Color::RED) {
        std::cout << "The color is red." << std::endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值