【C++】C++ 11 新特性:使用示例

文章目录

  • C++ 11 新特性
    • 变量类型推导 auto
    • 表达式类型推导 decltype
    • 初始化列表
    • 基于范围的for循环
    • Lambda 表达式
    • 智能指针
    • 空指针nullptr
    • 左值右值
    • 移动语义和完美转发
    • 常量表达式 constexpr
    • 委托构造函数
    • 继承构造函数
    • override
    • final
    • 并发编程
    • 正则表达式

C++ 11 新特性

以下内容给出C++11部分新特性的使用示例。

变量类型推导 auto

C++11引入了auto关键字,可以用于自动推导变量的类型,使得代码更加简洁和灵活。下面是一个简单的示例:

#include <iostream>
#include <vector>int main() {// 使用auto推导变量类型auto x = 5; // x被初始化为整数,因此其类型被推导为int。auto y = 3.14; // y被初始化为浮点数,因此其类型被推导为double。auto str = "Hello"; // str被初始化为字符串字面量,因此其类型被推导为const char*。// 使用了`auto`来声明循环迭代器`it`std::vector<int> numbers = {1, 2, 3, 4, 5};  // 初始化列表,见下文for (auto it = numbers.begin(); it != numbers.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

表达式类型推导 decltype

decltype用于推导表达式的类型。它可以用于推导变量的类型,也可以用于函数返回类型的推导:

推导变量类型

int x = 5;
decltype(x) y = x; // y的类型为int,与x相同

函数返回类型推导

int add(int a, int b) {return a + b;
}decltype(add(1, 2)) result; // result的类型为int,与add函数的返回类型相同

初始化列表

初始化列表特性它允许使用一种更简洁的语法来初始化容器、数组和类对象的成员。

在C++11之前,要初始化一个容器,需要使用一系列push_back()或insert()调用,比较繁琐。

#include <vector>
#include <iostream>int main() {std::vector<int> numbers;numbers.push_back(1);numbers.push_back(2);numbers.push_back(3);numbers.push_back(4);numbers.push_back(5);for (auto num : numbers) {std::cout << num << " ";}return 0;
}

使用初始化列表可以更加简洁地实现初始化。

#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5}; // 使用初始化列表初始化vectorfor (autonum : numbers) {std::cout << num << " ";}return 0;
}

这里,{1, 2, 3, 4, 5}就是一个初始化列表,用来初始化std::vector<int>容器。

基于范围的for循环

基于范围的for循环(range-based for loop)可以让遍历容器的操作更加简洁和直观。它使得循环体内可以直接访问容器中的元素,而不需要通过迭代器或者下标。

下面是一个使用基于范围的for循环遍历vector的例子:

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

另外,如果要修改vector中的元素,可以将num声明为引用类型:

#include <iostream>
#include <vector>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 使用基于范围的for循环遍历并修改vectorfor (int& num : numbers) {num *= 2;}// 输出修改后的vectorfor (int num : numbers) {std::cout << num << " ";}return 0;
}

这段代码会输出每个元素的两倍:

2 4 6 8 10

Lambda 表达式

Lambda表达式允许在代码中创建匿名函数,使得代码更加简洁和灵活。Lambda表达式的基本语法如下:

[捕获列表](参数列表) -> 返回类型 { 函数体 }

其中,[捕获列表]用于捕获外部变量,参数列表,返回类型和函数体与普通函数相似。

下面以求两个整数之和为例,演示Lambda表达式的使用:

#include <iostream>int main() {// Lambda表达式,用于求两个整数a和b的和auto add = [](int a, int b) { return a + b; };// 调用Lambda表达式std::cout << "Result: " << add(5, 3) << std::endl;return 0;
}

Lambda表达式还支持捕获外部变量。例如:

#include <iostream>int main() {int x = 10;// Lambda表达式,捕获外部变量x,并与另一个参数y相加auto add_with_x = [x](int y) { return x + y; };// 调用Lambda表达式std::cout << "Result: " << add_with_x(5) << std::endl;return 0;
}

智能指针

智能指针用于管理动态分配的内存的智能对象,可以在对象不再被使用时自动释放内存,避免了内存泄漏和悬空指针的问题。

C++11中最常用的智能指针包括std::shared_ptrstd::unique_ptrstd::weak_ptr

std::shared_ptr

std::shared_ptr是一种共享所有权的智能指针,多个std::shared_ptr对象可以共享同一个资源。当所有指向资源的std::shared_ptr都销毁时,资源才会被释放。
std::shared_ptr是一个引用计数智能指针,它在构造时会将引用计数初始化为 1,每当有新的 std::shared_ptr 指向相同的对象时,引用计数加 1,当有 std::shared_ptr 超出作用域或被显式置为 nullptr 时,引用计数减 1。当引用计数为 0 时,对象被销毁,释放内存。

#include <memory>
#include <iostream>int main() {std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(42);std::cout << sharedPtr1.use_count() << std::endl; // 引用计数:1std::shared_ptr<int> sharedPtr2 = sharedPtr1;std::cout << sharedPtr1.use_count() << std::endl; // 引用计数:2std::cout << *sharedPtr1 << std::endl; // 输出 42std::cout << *sharedPtr2 << std::endl; // 输出 42// 通过reset释放资源sharedPtr1.reset();std::cout << "sharedPtr1 is " << (sharedPtr1 ? "not null" : "null") << std::endl; // 输出 nullstd::cout << *sharedPtr2 << std::endl; // 输出 42,sharedPtr2仍然有效return 0;
}

std::unique_ptr

std::unique_ptr是一种独占所有权的智能指针,同一时间只能有一个std::unique_ptr指向资源。当std::unique_ptr被销毁时,它所拥有的资源也会被释放。

#include <memory>
#include <iostream>int main() {std::unique_ptr<int> uniquePtr1 = std::make_unique<int>(42);// std::unique_ptr<int> uniquePtr2 = uniquePtr1; // 错误!资源不可共享std::cout << *uniquePtr1 << std::endl; // 输出 42// 通过reset释放资源uniquePtr1.reset();std::cout << "uniquePtr1 is " << (uniquePtr1 ? "not null" : "null") << std::endl; // 输出 nullreturn 0;
}

std::weak_ptr

std::weak_ptr是一种弱引用智能指针,它不增加资源的引用计数。std::weak_ptr可以观察由std::shared_ptr管理的资源,但不会增加资源的引用计数,避免了循环引用的问题。

weak_ptr.lock() 方法用于获得对所指向对象的访问权。如果原来的 shared_ptr 对象还存在,则 lock() 方法返回一个有效的 shared_ptr 对象,如果原来的 shared_ptr 对象已经被销毁了(或者被置为 nullptr),则 lock() 方法返回一个空的 shared_ptr

#include <memory>
#include <iostream>int main() {std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);std::weak_ptr<int> weakPtr = sharedPtr;std::cout << sharedPtr.use_count() << std::endl; // 引用计数:1if (auto ptr = weakPtr.lock()) {std::cout << *ptr << std::endl; // 输出 42} else {std::cout << "sharedPtr is expired" << std::endl;}// 释放sharedPtrsharedPtr.reset();if (auto ptr = weakPtr.lock()) {std::cout << *ptr << std::endl; // 不会执行到这里,因为sharedPtr已经被释放} else {std::cout << "sharedPtr is expired" << std::endl; // 输出 sharedPtr is expired}return 0;
}

空指针nullptr

nullptr表示空指针常量。它的引入解决了传统的空指针表示方法NULL的一些问题,使代码更加清晰和安全。

在C++11之前,通常使用NULL来表示空指针。然而,NULL实际上是一个宏定义,其值通常是0或者(void*)0,这可能导致一些潜在的类型转换和歧义。例如:

void foo(int);
void foo(char*);foo(NULL);  // 调用foo(int),

NULL会被视为int类型,而不是char*类型,这可能导致程序出错。

使用nullptr可以解决这个问题。nullptr的类型是std::nullptr_t,它只能隐式转换为任意指针类型,而不能转换为整数类型。因此,上面的例子可以这样改写:

void foo(int);
void foo(char*);foo(nullptr);  // 调用foo(char*)

现在,nullptr明确表示一个空指针,不会引起类型转换的问题。

左值右值

左值和右值

  • 左值(lvalue):指向内存位置的表达式,可以出现在赋值号的左边或右边。

  • 右值(rvalue):临时值,它可以是字面量、临时对象或表达式求值的结果。

int x = 5;   // x是左值
int y = x + 3; // x + 3是右值

左值引用(&)和右值引用(&&

  • 左值引用(&):绑定到左值的引用。
  • 右值引用(&&):绑定到右值的引用。
int x = 5;
int& lref = x;  // lref是左值引用
int&& rref = 10; // rref是右值引用

移动语义和完美转发

移动语义(std::move()

移动语义允许在不进行深拷贝的情况下,将资源(例如堆上分配的内存)从一个对象转移到另一个对象。

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = std::move(v1); // 移动v1的资源到v2

在这里,std::move()v1的资源移动(类似电脑的ctrl+x)到v2中,而不是复制,从而提高了效率。

完美转发(std::forward()

完美转发可以让左右值参数传递给重载函数的时候,保持原来的参数类型。

template<typename T>
void wrapper(T&& arg) {process(std::forward<T>(arg));
}void process(int& x) {std::cout << "Processing lvalue\n";
}void process(int&& x) {std::cout << "Processing rvalue\n";
}int main() {int x = 5;wrapper(x);     // 输出 "Processing lvalue" 左值wrapper(10);    // 输出 "Processing rvalue" 右值
}

wrapper()函数使用了完美转发,它接受一个通用引用参数arg,然后使用std::forward()arg转发给process()函数,保持了参数的值类别。

常量表达式 constexpr

constexpr 用于声明常量表达式。它允许在编译时计算表达式的值,并在编译期间执行函数。

constexpr int square(int x) {return x * x;
}int main() {constexpr int result = square(5); // 在编译时计算static_assert(result == 25, "Incorrect square calculation");return 0;
}

委托构造函数

C++11引入了委托构造函数的概念,允许一个构造函数调用同一个类中的另一个构造函数,从而减少代码冗余。下面是一个简单的例子:

#include <iostream>class MyClass {
public:MyClass() : MyClass(0) { // 委托给带参数的构造函数std::cout << "Default constructor" << std::endl;}MyClass(int value) : m_value(value) {  // 带参数的构造函数std::cout << "Constructor with value " << m_value << std::endl;}private:int m_value;
};int main() {MyClass obj1; // 调用无参构造函数的同时也调用带参构造函数MyClass obj2(42); // 只是调用带参构造函数return 0;
}

输出结果为:

Constructor with value 0
Default constructor
Constructor with value 42

继承构造函数

继承构造函数的特性允许子类从基类中继承其所有构造函数。这使得代码更加简洁,避免了在子类中重新定义相同的构造函数。

以下是一个示例:

#include <iostream>class Base {
public:Base(int x) : value(x) {std::cout << "Base constructor" << std::endl;}void print() {std::cout << "Value: " << value << std::endl;}private:int value;
};class Derived : public Base {
public:using Base::Base; // 继承基类的构造函数void foo() {std::cout << "Derived function" << std::endl;}
};int main() {Derived d(10); // 使用继承的构造函数d.print(); // 调用基类的成员函数d.foo();   // 调用派生类的成员函数return 0;
}

override

override 关键字用于显式指示一个成员函数覆盖了基类中的虚函数,它可以帮助检测错误,如果子类中的函数并没有正确地覆盖基类中的虚函数,编译器将产生错误。

class Base {
public:virtual void foo() const {std::cout << "Base::foo()" << std::endl;}
};class Derived : public Base {
public:void foo() const override { // 使用override关键字std::cout << "Derived::foo()" << std::endl;}
};int main() {Derived d;Base* ptr = &d;ptr->foo(); // 输出 Derived::foo()return 0;
}

如果在 Derived 类中的 foo() 函数没有正确地覆盖基类中的虚函数,例如拼写错误或者参数不匹配,编译器将会提示错误。

final

final 关键字用于标记一个虚函数不能被子类再次覆盖,或者一个类不能被继承。这有助于防止意外的覆盖或继承。

class Base {
public:virtual void foo() const final { // 使用final关键字std::cout << "Base::foo()" << std::endl;}
};class Derived : public Base {
public://void foo() const override; // 试图覆盖基类中的foo()函数,会导致编译错误
};class Derived2 final : public Base { // 使用final关键字,Derived2类不能再被继承
};// class Derived3 : public Derived2 {}; // 试图继承Derived2类,会导致编译错误int main() {Derived d;Base* ptr = &d;ptr->foo(); // 输出 Base::foo()return 0;
}

并发编程

推荐阅读:C++ 并发编程(从C++11到C++17)

正则表达式

推荐阅读:https://www.cnblogs.com/coolcpp/p/cpp-regex.html

参考:
c++11新特性,所有知识点都在这了!
现代 C++ 教程:高速上手 C++11/14/17/20 欧长坤

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/5897.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

liceo靶机复现

liceo-hackmyvm 靶机地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmLiceo 本机环境&#xff1a;NAT模式下&#xff0c;使用VirtualBox 信息收集&#xff1a; 首先局域网内探测靶机IP 发现IP为10.0.2.4 开启nmap扫描一下看看开了什么端口 扫描期间看一下web页…

粤嵌gec6818开发板-驱动usb摄像头

前段时间做了一个项目&#xff0c;用到了linux环境下gec6818开发板驱动usb摄像头&#xff0c;在这里给大家分享一下。 摄像头的操作步骤 1&#xff09;打开设备 2&#xff09;配置设备&#xff08;采集的频率、图像宽高、图像格式&#xff09; 3&#xff09;在内核空间申请缓冲…

[蓝桥杯2024]-PWN:fd解析(命令符转义,标准输出重定向,利用system(‘$0‘)获取shell权限)

查看保护 查看ida 这里有一次栈溢出&#xff0c;并且题目给了我们system函数。 这里的知识点没有那么复杂 方法一&#xff08;命令转义&#xff09;&#xff1a; 完整exp&#xff1a; from pwn import* pprocess(./pwn) pop_rdi0x400933 info0x601090 system0x400778payloa…

78、贪心-跳跃游戏

思路 方法1: canJump01 - 使用递归&#xff08;回溯法&#xff09; 这个方法是通过递归实现的&#xff0c;它从数组的第一个位置开始&#xff0c;尝试所有可能的跳跃步数&#xff0c;直到达到数组的最后一个位置或遍历完所有的可能性。 思路&#xff1a; 如果数组为空或者长…

【docker】docker compose 搭建私服

安装 Docker Registry 创建目录 mkdir -pv /usr/local/docker/registrymkdir -pv /usr/local/docker/data 创建 docker-compose.yml文件 进入目录创建docker-compose.yml cd /usr/local/docker/registrytouch docker-compose.yml 编辑docker-compose.yml vim docker-compo…

2024年【起重机械安全管理】考试内容及起重机械安全管理操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械安全管理考试内容根据新起重机械安全管理考试大纲要求&#xff0c;安全生产模拟考试一点通将起重机械安全管理模拟考试试题进行汇编&#xff0c;组成一套起重机械安全管理全真模拟考试试题&#xff0c;学员可…

52.HarmonyOS鸿蒙系统 App(ArkTS)配置文件添加多个权限方法

52.HarmonyOS鸿蒙系统 App(ArkTS)配置文件添加多个权限方法 module.json5

VS2022 嘿嘿

还是大二的时候就开始用这个&#xff0c;但居然是为了用PB&#xff0c;-_-|| 用了段时间换成了C#&#xff0c;依稀还记得大佬们纠正我的读法&#xff0c;别读C井&#xff0c;应该读C夏普。。。 安装过程其实也没啥&#xff0c;就是关键Key得花时间找&#xff0c;我好不容易搞…

Concise CoT(CCoT)提示词工程

原文地址&#xff1a;concise-chain-of-thought-ccot-prompting 2024 年 1 月 24 日 传统的 CoT 是以增加输出令牌使用为代价的&#xff0c;CCoT 提示是一种提示工程技术&#xff0c;旨在减少 LLM 响应的冗长和推理时间。 基于LLMs的生成式人工智能应用程序必须使用多管齐下的方…

【Linux】理解 Ubuntu 中的 kill 和 killall 命令

我把我唱给你听 把你纯真无邪的笑容给我吧 我们应该有快乐的 幸福的晴朗的时光 我把我唱给你听 用我炙热的感情感动你好吗 岁月是值得怀念的留恋的 害羞的红色脸庞 谁能够代替你呀 趁年轻尽情的爱吧 最最亲爱的人啊 路途遥远我们在一起吧 &#x1f3b5; 叶…

php使用Canal监听msyql

canal需要java8 去官网下载java8 安装JAVA #创建目录 mkdir -p /usr/local/java/ #解压到目录 tar zxvf jdk-8u411-linux-x64.tar.gz -C /usr/local/java/配置环境变量在 /etc/profile 最后加入 export JAVA_HOME/usr/local/java/jdk1.8.0_411 export CLASSPATH.:$JAVA_HOM…

【ZZULIOJ】1092: 素数表(函数专题)(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 输入两个正整数m和n&#xff0c;输出m和n之间的所有素数。 要求程序定义一个prime()函数和一个main()函数&#xff0c;prime()函数判断一个整数n是否是素数&#xff0c;其余功能在main()函数中实现。…

牛客NC320 装箱问题【中等 动态规划,背包问题 C++/Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/d195a735f05b46cf8f210c4ad250681c 几乎完全相同的题目&#xff1a; https://www.lintcode.com/problem/92/description 思路 动态规划都是递归递推而来。php答案是动态规划版本&#xff0c;递归版本有 测试用…

uint32_t与int区别与联系

1.背景介绍 在一些程序中经常看到uint8_t uint32_t等数据类型&#xff0c;那么它与我们常见的int类型有什么区别呢。 2.协议介绍 stdint.h头文件是为了代码的可移植性而推出的&#xff0c;C99中就已经规范了。 3.代码可移植性 3.1 数据类型的差异 大部分的32位系统采用的…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt+hadoop + redis 医院就诊系统 设计与实现

一.项目介绍 前端&#xff1a;患者注册 、登录、查看首页、医生排班、药品信息、预约挂号、就诊记录、电子病历、处方开药、我的收藏 后端分为&#xff1a; 医生登录&#xff1a;查看当前排班信息、查看患者的挂号情况、设置患者就诊记录、电子病历、给患者开药和个人信息维护 …

公共交通无障碍设施:科技翅膀助力盲人出行新飞跃

在城市的脉络中&#xff0c;公共交通扮演着连接每一个角落的重要角色。然而&#xff0c;对于视力受限的盲人朋友而言&#xff0c;这幅繁忙而复杂的交通网络往往隐藏着诸多不易察觉的障碍。值得庆幸的是&#xff0c;随着公共交通无障碍设施的不断完善&#xff0c;以及高科技辅助…

20240502给NanoPi的NEO core开发板编译移远的4G模块的上网程序quectel-CM

20240502给NanoPi的NEO core开发板编译移远的4G模块的上网程序quectel-CM 2024/5/2 16:29 1、默认编译为AMD64/INTEL的x64架构的可执行文件&#xff1a; rootrootrootroot-ThinkBook-16-G5-IRH:~$ rootrootrootroot-ThinkBook-16-G5-IRH:~$ unzip Quectel_QConnectManager_Lin…

CSS-复合选择器

作用&#xff1a; 后代选择器&#xff1a; 子代选择器 并集选择器 用逗号隔开&#xff0c;在style里面写的时候&#xff0c;每一个标签空一行。 <title>Document</title><style>p,div,span{color: aqua;}</style> </head> <body><p>…

C语言——队列的实现

队列按照先进先出&#xff08;FIFO&#xff0c;First In First Out&#xff09;的原则管理数据。这意味着最先进入队列的元素会被最先移出&#xff0c;类似于排队等候服务的情况。队列通常有两个主要操作&#xff1a;入队&#xff08;enqueue&#xff09;&#xff0c;将元素添加…

python - 3D图表绘制

Pyecharts 和 3D 图表绘制 Pyecharts 是一个用于生成各种图表的 Python 库&#xff0c;它基于 Echarts&#xff0c;支持大量的图表类型&#xff0c;非常适合用于数据分析和可视化。Pyecharts 主要优点是易于使用&#xff0c;可以直接在 Python 环境中绘制富有交互性的图表&…