目录
前言:
类型推导:
constexpr关键字:
初始化列表:
基于范围的for循环:
智能指针之unique ptr
Lambda表达式:
总结:
前言:
本文我们将继续介绍 C++ 11 新增十大特性的剩余六个,如果没有看过介绍前四个特性的小伙伴的可以点进我C++的专栏就可以看到。
类型推导:
类型推导(type inference)是 C++11 引入的一个重要特性,表示编译器可以根据上下文推断变量或表达式的类型。也就是说,在某些情况下,可以省略类型声明,直接使用关键字 auto 来定义变量或表达式,而编译器会自动推导该变量或表达式的类型。
使用类型推导可以减少代码中的重复性,并且可以使代码更加简洁和易读。使用类型推导时应该注意以下几点:
1. auto 关键字只能用于定义局部变量,不能用于定义类成员变量或全局变量。
2. 使用 auto 定义变量时,默认情况下变量是 const 类型,如果要定义非 const 类型变量,可以使用关键字 const_cast 来进行强制类型转换。
3. 由于编译器需要通过上下文推断变量类型,因此 auto 变量的初始化表达式必须完全明确,不能存在二义性。如果初始化表达式中有重载函数,编译器会根据函数的返回值类型来进行类型推导。
4. 在定义 auto 变量时,可以使用 decltype 关键字来指定变量的类型。
以下是一些使用类型推导的示例:
auto i = 10; // 推导出 i 的类型是 int
auto p = new int[10]; // 推导出 p 的类型是 int*
auto s = "hello"; // 推导出 s 的类型是 const char*
auto fun = [](int x) { return x + 1; }; // 推导出 fun 的类型是 lambda 表达式类型
总之,在某些情况下,使用类型推导可以使代码更加简洁明了,并且在降低代码重复性的同时还能提高代码可读性。注意需要谨慎使用类型推导,特别是在代码复杂或可读性要求较高的场合。
constexpr关键字:
constexpr 是一种 C++11 新增的关键字,用于使编译器在编译时计算常量表达式,从而提高代码效率。
在函数或变量声明时,constexpr 关键字可以用于声明一个常量表达式,例如:
constexpr int MAX_LEVEL = 100; // 声明一个常量表达式
constexpr int factorial(int n) { // 声明一个返回值为常量表达式的函数
return (n <= 1) ? 1 : n * factorial(n - 1);
}
使用 constexpr 关键字声明时,编译器会在编译时对表达式进行求值,而不是在程序运行时对其进行计算。这样可以在编译时就确定常量值,从而在运行时提高代码效率。
在 C++11 中,使用 constexpr 关键字定义常量表达式时,必须满足以下条件:
1. 表达式必须是一个纯量表达式。
2. 表达式的结果必须在编译时可知。
3. 表达式的求值必须产生一个值而不是一个副作用。
4. 函数返回值为 constexpr 类型的函数必须是由成文定义而非 inline 定义的。
使用 constexpr 关键字定义常量表达式的好处包括:
1. 编译时求解:由于在编译时计算常量表达式,可以避免在程序运行时进行计算,从而提高程序的运行效率。
2. 类型安全:使用 `constexpr` 关键字定义的常量表达式具有固定的类型,可以帮助程序员避免类型转换错误和运行时错误。
3. 代码简洁:使用 `constexpr` 关键字定义常量表达式可以简化代码,从而使程序更加易读易懂。
综上所述,`constexpr` 是非常有用的 C++11 特性,可以帮助程序员编写更高效、更安全的代码,在实际编程中应该加以充分利用。
这是一个很实用的关键字,用好了可以直接让性能起飞。
但其实他还有一个很实用的应用:实现动态数组。
因为他是在编译阶段就完成计算了,因此可以将他作为数组的大小参数而不会报错。这样我们就实现了动态数组。
constexpr int pow (int x,int y)
{
int result=x;
while(--y>0)
{
result *=x;
}
return result;
}
int main()
{
int a[pow(2,4)]
//我们在这里不就实现了动态数组。
}
初始化列表:
C++11 引入了初始化列表(initializer list)的概念,可以用来初始化一些标准容器、数组和自定义类型等对象。
初始化列表使用花括号 {} 来表示,可以在花括号中列出需要初始化的元素。以下是一些初始化列表的示例:
std::vector<int> v {1, 2, 3}; // 初始化一个包含三个元素的 vector 对象
int numbers[] {1, 2, 3}; // 初始化一个包含三个元素的整数数组
struct Point {
int x;
int y;
};
Point p {3, 4}; // 初始化一个自定义类型对象
初始化列表的好处主要有以下几点:
1. 初始化列表可以通过列表方式快速创建并初始化一个标准容器、数组或自定义类型的对象,简化了代码。
2. 初始化列表可以派生两个对象的初始化,例如可以使用首项列表来初始化一个 std::pair 对象,这样可以非常方便地初始化复合对象。
3. 初始化列表可以保证元素的顺序准确无误,而且可以确保元素的数量正确,避免了遗漏或重复,从而提高了程序的安全性。
需要注意的是,初始化列表不仅可以用于创建新对象,还可以用于赋值操作。例如,可以使用初始化列表来替代传统的赋值操作:
std::vector<int> v;
v = {1, 2, 3}; // 使用初始化列表来替代传统的赋值操作
总之,初始化列表是一个非常有用的 C++11 特性,可以简化代码的编写,避免了繁琐的初始化操作,并且提供了一种简单、可靠的方式来初始化容器、数组和自定义类型等对象。在实际编程中应该充分利用该特性。
基于范围的for循环:
C++11 引入了基于范围的 for 循环(range-based for loop)的特性,其语法形式为:
for (element : range) {
// 循环体内容
}
其中,range 是一个 range 类别对象,element 表示从 range 中遍历出来的每个元素值。在循环体中可以直接使用 element 变量完成相应的操作。
使用基于范围的 for 循环的好处主要有以下几点:
1. 简化了代码:基于范围的 for 循环可以直接遍历数组和标准容器,避免了循环计数器的管理和手动访问容器或数组的元素,从而简化了代码。
2. 可靠性更高:基于范围的 for 循环可以避免越界访问和逻辑错误,从而提高了代码的可读性和可靠性。
3. 支持自定义类型:可以自定义一个类,从而实现让该类具有 range 类别的特性,然后可以在基于范围的 for 循环中使用该类的实例。
以下是一些基于范围的 for 循环的示例:
std::vector<int> v {1, 2, 3, 4, 5};
for (int i : v) {
std::cout << i << " ";
}
int a[] {1, 2, 3, 4, 5};
for (int i : a) {
std::cout << i << " ";
}
std::string str = "Hello, world!";
for (char c : str) {
std::cout << c << " ";
}
总之,基于范围的 for 循环是 C++11 的一项非常实用的特性,可以大大简化代码,提高代码的可读性和可靠性。在实际编程中应该充分掌握并应用该特性。
智能指针之unique ptr
需要头文件 #include <memory>
智能指针是 C++11 引入的一个非常有用的特性,可以管理动态内存分配,避免内存泄漏和空指针的问题。其中,unique_ptr 是一种独占所有权的智能指针,用于管理动态分配的对象,具有以下特点:
1. unique_ptr 持有对象的所有权,即只有一个 unique_ptr 可以管理一个对象。
2. unique_ptr 通过 RAII(Resource Acquisition Is Initialization)机制来管理动态分配的对象,即在 unique_ptr 对象生命周期结束时,会自动释放所管理的对象。
3. unique_ptr 不支持共享所有权,即不能通过复制或赋值的方式将 unique_ptr 对象的所有权转移给其他 unique_ptr 对象。
以下是一些 unique_ptr 的使用示例:
std::unique_ptr<int> p(new int(10)); // 创建一个 unique_ptr
if (p) { // 判断指针是否为空
std::cout << *p << std::endl; // 输出指针所指向的对象值
}
*p = 20; // 通过指针修改所指向对象值
std::unique_ptr<int> p2(std::move(p)); // 转移指针所有权
if (p2) { // 判断指针是否为空
std::cout << *p2 << std::endl; // 输出指针所指向的对象值
}
释放 p2 管理的对象
p2.reset();
使用自定义删除器来释放动态分配的对象
std::unique_ptr<int, void(*)(int*)> p3(new int(30), [](int* p) { delete p; });
总之,unique_ptr 是 C++11 引入的一种非常有用的独占所有权智能指针,可以有效解决动态内存分配管理问题,规避内存泄漏和空指针的风险,提高程序的稳定性和可靠性。在实际编程中,应该充分掌握并应用 unique_ptr。
其实 unique_ptr 简而言之就是可以自动释放内存,避免了内存泄漏的风险。
#include<memeory>
using namespace std;
struct data
{
int a,b,c;
};
void (f)
{
unique_ptr<data> data (new data)
{
data->a=10;
data->b=20;
data->c=30;
}
}
我们在这里就不需要手动释放内存。此外,我们还提供了另外一种方法构建一个智能指针:
auto data = make_unique<data>();
这种写法避免了因为 data 构建失败而 抛出异常之后 出现 野指针。
关于智能指针其实还有很多知识点,我们在这里只是给大家简单讲解了一种智能指针。在后面会专门为大家写一篇文章讲解。
Lambda表达式:
Lambda 表达式是 C++11 引入的一种函数对象,主要用于简化函数对象的定义和传递。
Lambda 表达式由三部分组成:
1. 捕获列表(capture list):用于捕获一些外部变量或对象,可以为空。
2. 形参列表(parameter list):用于定义函数对象的形参列表,可以为空。
3. 函数体(function body):用于定义函数对象的实现,可以为空。
Lambda 表达式的定义方式为:
[capture list](parameters) -> return-type { function body }
其中,capture list 和 return-type 都是可选的,而 parameters 和 function body 则是必须的。以下是一些 Lambda 表达式的示例:
// 定义一个 Lambda 表达式
auto func = [](int x, int y) -> int { return x + y; };
// 使用 Lambda 表达式
int result = func(1, 2);
std::cout << result << std::endl;
// 定义一个带捕获列表的 Lambda 表达式
int a = 1, b = 2;
auto func2 = [a, &b](){ std::cout << a << b << std::endl; };
// 使用带捕获列表的 Lambda 表达式
func2();
Lambda 表达式的好处主要有以下几点:
1. 简化函数对象的定义:Lambda 表达式可以直接在需要的地方定义函数对象,避免了传统定义函数对象的繁琐和冗余。
2. 方便函数对象的传递:Lambda 表达式可以作为函数对象进行传递,从而避免了手动定义函数对象并传递的过程。
3. 支持捕获外部变量或对象:通过捕获列表,Lambda 表达式可以直接捕获外部作用域的变量或对象,方便编程。
总之,Lambda 表达式是 C++11 中非常实用的一项特性,可以简化函数对象的定义和传递,极大地提高了代码编写效率和代码可读性。在实际编程中应该充分掌握并应用该特性。
总结:
本文我们详细的介绍了剩余的六个新增特性,其中有不少都是很实用的特性,如果用的好了可以大大简化代码的重复以及精简结构,因此我们要熟练的掌握这十个特性。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!