目录
C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战进阶(专栏文章已更新390多篇,持续更新中...)
https://blog.csdn.net/chenlycly/category_11931267.htmlWindows C++ 软件开发从入门到精通(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html C++14标准在2014年8月18日被C++标准委员会正式批准宣布,同年12月15日正式发布release版本,作为C++11标准的一个较小但重要的补充和改进,它在保持向后兼容性的同时,引入了一系列旨在提高代码简洁性和效率的新特性和增强功能。下面介绍一下C++14的常用新特性。
- 很多C++开源库会大规模地使用C++11及以上的新特性,比如WebRTC开源库,所以在阅读开源代码时需要了解C++11及以上新特性。我们在日常编码中会用到部分新特性,比如一些新的关键字(auto、nullptr、override、final等)、lambda表达式(匿名函数)、智能指针等。此外,现在很多C++开发岗位在面试时都会问到C++11或以上的新特性方面的内容。所以学习C++11及以上的新特性很有必要!
- C++新标准引入的诸多新特性,解决了语言上的部分缺陷,使得C++更得加高效灵活!但也使得C++变得更加臃肿复杂,更加难以驾驭!
1、通用lambda表达式(对lambda表达式的改进)
C++14引入了通用lambda表达式,可以使用auto关键字作为参数类型和返回类型,使得lambda表达式更加灵活。
通用lambda表达式的语法如下:
[ captures ] ( auto&&... params ) -> decltype(auto) { body }
其中,captures是lambda表达式的捕获列表,params是lambda表达式的参数列表,decltype(auto)表示返回类型会根据body自动推导出来。
对C++11中的Lambda表达式的改进,主要体现在以下两个方面:
- 初始化捕获:在C++14中,lambda表达式的捕获列表支持直接初始化捕获的变量,这意味着可以在定义lambda时直接为捕获的变量赋予初始值,而无需在外部提前声明。
- 泛型Lambda:C++14允许在Lambda表达式中使用auto作为参数类型,这使得Lambda可以更加灵活地处理不同类型的参数,而无需显式指定类型。
例如,以下是一个使用通用lambda表达式的例子:
#include <iostream>
int main()
{
auto sum = [](auto x, auto y)
{
return x + y;
};
std::cout << sum(2, 3) << std::endl; // 输出 5
std::cout << sum(1.0, 2.5) << std::endl; // 输出 3.5
return 0;
}
在这个例子中,lambda表达式的参数类型和返回类型都使用了auto关键字,使得它可以接受不同类型的参数,并返回相应的结果。
2、constexpr常量表达式
C++14中,常量表达式是指在编译时可以计算出结果的表达式,它可以用于声明常量、数组大小、枚举值等。C++14中新增了一些常量表达式的规则:
1)函数可以被声明为常量表达式,只要函数满足以下条件:
a)函数的返回值类型是字面类型(literal type)
b)函数体只包含符合常量表达式要求的语句
2)可以使用if和switch语句,只要它们的条件表达式是常量表达式,并且语句体也是符合常量表达式要求的语句。
3)可以使用循环语句,只要循环次数是常量表达式。
4)可以使用lambda表达式,只要它符合常量表达式的要求。
下面是一个使用常量表达式的例子:
constexpr int factorial(int n)
{
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main()
{
constexpr int n = 5;
int arr[factorial(n)]; // 使用常量表达式计算数组大小
return 0;
}
在上面的例子中,函数factorial被声明为常量表达式,并用于计算数组arr的大小。由于n是一个编译时常量,因此可以在编译时计算出factorial(n)的值,从而确定数组的大小。
打印到控制台上。print函数创建一个std::tuple对象,并使用std::make_index_sequence来创建一个std::index_sequence对象,然后将它们传递给print_helper函数。foo函数使用print函数来打印参数。
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该精品技术专栏的订阅量已达到500多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏3:(本专栏涵盖了多方面的内容,是当前重点打造的专栏,专栏文章已经更新到390多篇,持续更新中...)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5:
Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了Windows C++ 应用软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
3、constexpr函数的扩展
C++11引入了constexpr函数的概念,这样的函数可以在编译期执行。然而,C++11要求constexpr函数只含有一个将被返回的表达式。C++14放宽了这些限制,允许constexpr函数中使用局部变量、循环和条件语句等,只要最终的结果能够在编译时计算得出即可。
C++14中对constexpr函数的扩展主要包括以下几个方面:
1)放宽了对constexpr函数的限制:在C++11中,constexpr函数只能包含一些简单的语句,比如赋值语句和return语句,而在C++14中,constexpr函数可以包含一些复杂的语句,比如if语句和循环语句。
2)允许constexpr函数调用非constexpr函数:在C++11中,constexpr函数只能调用其他constexpr函数,而在C++14中,constexpr函数可以调用非constexpr函数,只要这些函数的返回值可以在编译时确定。
3)允许constexpr函数返回void类型:在C++11中,constexpr函数必须返回一个常量表达式,而在C++14中,constexpr函数可以返回void类型,只要函数体中的语句都是常量表达式。
4)允许constexpr函数有多个参数:在C++11中,constexpr函数只能有一个参数,而在C++14中,constexpr函数可以有多个参数,只要这些参数都是常量表达式。
5)允许constexpr函数有局部变量:在C++11中,constexpr函数不能有局部变量,而在C++14中,constexpr函数可以有局部变量,只要这些变量都是常量表达式。
总的来说,C++14中对constexpr函数的扩展使得这种函数更加灵活和实用,可以用于更多的场景,提高代码的可读性和可维护性。
4、二进制字面量
C++14引入了二进制字面量,允许程序员使用二进制表示法来表示整数值。二进制字面量以前缀0b或0B开头,后面跟着一串二进制数字。例如,0b111111表示十进制数63。并且允许在数字字面量中使用单引号作为分隔符,以提高代码的可读性。
以下是一个简单的示例:
#include <iostream>
int main()
{
int i = 0b111111;
std::cout << i << std::endl; // 输出63
return 0;
}
二进制字面量提供了一种简单而方便的方法来表示位模式,这对于编写低级别的系统代码或进行位运算非常有用。
5、数组大小自动推导
在C++14中,可以使用auto关键字和初始化列表来实现数组大小的自动推导。具体来说,可以使用以下语法:
auto arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 自动推导为std::initializer_list<int>
在这个例子中,编译器会自动推导出arr的类型为std::initializer_list< int >,而数组的大小也会自动根据初始化列表的元素个数进行推导。因此,上述代码等价于下面的代码:
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 数组大小为10
需要注意的是,这种自动推导方式只适用于静态数组,而对于动态数组来说,还需要使用new运算符手动分配内存。另外,由于std::initializer_list是一个轻量级的容器,因此它的性能可能不如普通数组。
6、std::make_unique
C++14中的std::make_unique是一个函数模板,用于创建一个std::unique_ptr对象并将其初始化为一个新对象。它接受一个可变参数列表和一个构造函数的参数列表,用于在创建新对象时传递给构造函数。
make_unique的语法如下:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
其中,T是要创建的对象的类型,Args是传递给构造函数的参数列表。make_unique返回一个std::unique_ptr对象,该对象拥有指向新对象的所有权。
使用make_unique可以避免手动创建std::unique_ptr对象并进行new操作,从而避免了内存泄漏和错误的可能性。它还可以提高代码的可读性和简洁性。以下是一个使用make_unique创建一个动态分配的对象的例子:
#include <memory>
#include <iostream>
class TestClass
{
public:
TestClass(int value) : m_value(value)
{
std::cout << "TestClass constructor called with value: " << m_value << std::endl;
}
~TestClass()
{
std::cout << "TestClass destructor called with value: " << m_value << std::endl;
}
private:
int m_value;
};
int main() {
auto ptr = std::make_unique<TestClass>(20);
return 0;
}
在这个例子中,我们使用make_unique创建了一个动态分配的MyClass对象,并将其初始化为值20。当程序退出main函数时,指向该对象的指针ptr将被自动销毁,并调用MyClass的析构函数。
需要注意的是,make_unique不能用于创建数组,因为std::unique_ptr不支持动态数组。如果需要创建动态数组,应该使用std::vector或std::array。
7、std::exchange
std::exchange是C++14中引入的一个函数模板,它定义在头文件中。这个函数的作用是交换一个对象的值并返回其旧值。
std::exchange的函数原型如下:
template<class T, class U = T>
T exchange(T& obj, U&& new_value);
其中,T是要交换值的对象的类型,obj是要交换值的对象的引用,new_value是新值,U是新值的类型。
这个函数的作用是将obj的值用new_value替换,并返回obj原来的值。这个操作是原子的,所以在多线程环境中使用是安全的。以下是一个使用std::exchange的例子:
#include <iostream>
#include <utility>
int main()
{
int x = 1;
int y = std::exchange(x, 2);
std::cout << "x = " << x << ", y = " << y << std::endl; // 输出x = 2, y = 1
return 0;
}
在这个例子中,我们使用std::exchange将x的值从1替换成2,并将原来的值1赋给了y。
8、std::integer_sequence
C++14中的std::integer_sequence是一个模板类,用于创建一个整数序列。它可以用于编写与模板参数数量和类型无关的代码,例如元编程和模板元函数。
std::integer_sequence有两个模板参数:第一个是整数类型(通常是std::size_t),第二个是整数序列的长度。例如,std::integer_sequence<std::size_t, 3>表示一个包含三个std::size_t类型整数的序列。
std::make_integer_sequence模板函数可以用来创建一个整数序列。它接受一个整数类型和一个整数序列长度作为参数,并返回一个std::integer_sequence对象。例如,std::make_integer_sequence<std::size_t, 5>将返回一个包含0到4的std::size_t类型整数的序列。
std::index_sequence是std::integer_sequence的特化版本,其中第一个模板参数固定为std::size_t。它通常用于访问元组中的元素,因为元组中的元素是按照索引顺序存储的。
使用std::integer_sequence和std::make_integer_sequence可以实现可变参数模板的参数展开,例如:
template<typename... Ts>
void foo(Ts... args)
{
bar(std::make_index_sequence<sizeof...(Ts)>{}, args...);
}
template<typename... Ts, std::size_t... Is>
void bar(std::index_sequence<Is...>, Ts... args)
{
// 访问args中的元素,例如:
int x = std::get<Is>(std::make_tuple(args...));
}
在上面的例子中,foo函数接受任意数量和类型的参数,并将它们传递给bar函数。bar函数使用std::index_sequence来访问args中的元素。
#include <iostream>
#include <utility>
template<typename... Ts, std::size_t... Is>
void print_helper(const std::tuple<Ts...>& tpl, std::index_sequence<Is...>)
{
((std::cout << std::get<Is>(tpl) << ' '), ...);
std::cout << '\n';
}
template<typename... Ts>
void print(Ts... args)
{
std::tuple<Ts...> tpl(args...);
print_helper(tpl, std::make_index_sequence<sizeof...(Ts)>());
}
template<typename... Ts>
void foo(Ts... args)
{
print(args...);
}
int main()
{
foo(1, 1.5, "Hello World"); // 输出:1 1.5 Hello World
return 0;
}
在上面的例子中,print_helper函数使用std::index_sequence来展开std::tuple中的元素,并将它们
9、变长参数模板的扩展
C++14中引入了变长参数模板的扩展,可以使用类似于函数参数的语法来定义模板参数列表。这个特性被称为“参数包扩展”或“参数模板扩展”。参数模板扩展允许在模板参数列表中使用省略号(…)来表示一个可变数量的模板参数。这些参数被称为“参数包”,可以在模板定义中使用。
例如,下面的代码定义了一个可变参数模板,用于在编译时计算一组数字的总和:
template<typename... Args>
int sum(Args... args)
{
return (args + ...);
}
在这个例子中,省略号表示Args是一个可变数量的模板参数。在函数体中,使用了折叠表达式(fold expression)来计算所有参数的总和。
使用参数模板扩展可以极大地简化代码,特别是在处理不同数量的参数时。例如,可以定义一个可变参数模板来打印任意数量的值:
template<typename... Args>
void print(Args... args)
{
(std::cout << ... << args) << '\n';
}
在这个例子中,省略号表示Args是一个可变数量的模板参数。在函数体中,使用了折叠表达式来将所有参数输出到标准输出。