C++11标准公布后,C++社群中出现了久违的热情,有人甚至叫出“C++的复兴”。指望C++重回20世纪90年代中期那样的地位显然是昧于大势的期望,但是C++经历了这么多年的打磨与起伏,在各领域的地位已经非常稳固了。2011年新标准的出现能够大大提升C++开发的效率和质量。时至今日,能够基本了解C++11标准的程序员恐怕不多,而能够以新的C++风格开发实践的人更是凤毛麟角。因此,接下来几节,我们将盘点C++11的新特性。
作为开幕第一篇,我们先说一些我们之前用到的但是你不知道是C++11标准的新特性。
1.long long类型
C++定义了一套包括算术类型(arithmetic type)和空类型(void)在内的基本数据类型。其中算数类型包含了字符、整型数、布尔值和浮点数。空类型不对应具体的值,仅用于一些特殊的场合,例如最常见的就是当函数不返回任何值时使用void 作为返回类型。
算术类型分为两类:整型(包括字符和布尔类型在内)和浮点型。算术类型的大小在不同的机器上有所差别。下面给出C++标准规定的尺寸的最小值。
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽字符 | 16位 |
char16_t | Unicode字符 | 16位 |
char32_t | Unicode 字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32位 |
long long | 长整型 | 64位 |
float | 单精度浮点数 | 6位有效数字 |
double | 双精度浮点数 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位有效数字 |
C++规定一个int至少要和一个short一样大,一个long至少要和一个int一样大,一个long long至少要和long一样大。其中,数据类型long long 是在C++11中新定义的。
使用
long long
可以帮助处理需要大数值计算的场景,例如在金融应用,科学计算和某些算法中
2.列表初始化
C++语言定义了初始化的好几种不同形式,这也是初始化问题复杂性的一个体现。例如,想要定义一个int型的变量test_a并初始化为0,以下四条语句都可以做到这一点:
int test_a = 0;
int test_a = {0};
int test_a{0};
int test_a(0);
作为C++11新标准的一部分,用花括号{ }来初始化变量得到了全面应用 ,而在此之前,这种初始化的形式仅在某些受限的场合下才能使用。这种初始化的形式被称为列表初始化。现在,无论是初始化对象还是某些时候为对象赋值,都可以使用这样一组花括号括起来的初始值了。
3.nullptr常量
空指针不指向任何对象,在试图使用一个指针之前 代码可以首先检查它是否为空。以下列出几个可以生成空指针的方法。
int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL;//需要包含头文件<cstdlib>/<stdlib.h>
得到空指针最直接的办法就是使用C++新标准引入的nullptr字面值。 nullptr是一种特殊类型的字面值,它可以被转化成任意其他的指针类型。在这之前的程序会用到第三种方法:使用一个名为NULL的预处理变量来给指针赋值。但是当用到一个预处理变量时,预处理器会自动地将它替换为实际值,因此用NULL初始化指针和用0初始化指针是一样的。在新标准下,现在的C++程序最好使用nullptr,并且避免使用NULL。
4.类型别名声明
类型别名是一个名字,他是某种数据类型地同义词。使用类型别名有很多好处,他让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚的知道使用该类型地真实目的。一般来讲有三种方法定义类型别名。
第一种#define:
#define Long long long
此处将long long类型起了个别名Long。
第二种typedef:
typedef double wages;
typedef wages* pd;
此处wages是double类型,pd是double*类型。
关键字typedef作为声明语句中的基本数据类型的一部分出现。含有typedef的声明语句定义的不再是变量而是类型别名。和以前的声明语句一样,这里的生命夫包含类型修饰,从而由基本数据类型构造出复合类型来。
typedef const T* const_iterator;
第三种using:
using myType = int;
此处将int起别名为myType。
这种方法用关键字using作为别名声明的开始,其后,紧跟别名和等号,其作用是把等号左侧的名字规定成右侧类型的别名。
5.auto类型指示符
编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚的知道表达式的类型。然而要做到这一点并非易事,有时根本做不到。为了解决这个问题,C++11新标准引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。和原来那些只应对一种特定类型 的说明符不同,auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须是有初始值的:
auto x = 42; // x为int类型
auto y = 3.14; // y为double类型
auto z = "Hello"; // z为const char*
复合类型、常量和auto: 编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当的改变结果类型使其更符合初始化规则。最典型的例子就是引用类型:
int i=0 , &r=i;
auto a = r;
在这个例子中,r是i的引用,将r赋给a时,a的类型是i的int类型,而不是r的int&引用类型。但是我们可以使用auto&来进行处理这类问题: 此时a就是f的引用类型了。
double f=0.0;
auto& a = f;
6.范围for语句
C++11 引入的范围基于的 for 循环(range-based for loop)使得遍历容器变得更加简洁和方便。它允许开发者以非常直观的方式遍历数组、容器(如 std::vector
, std::list
, 等),以及任何实现了 begin()
和 end()
方法的类型。
范围 for 循环的基本语法如下:
for (declaration : collection) { // 对每一个 element 执行的操作
}
declaration:用于声明循环中每个元素的变量,通常使用 auto
关键字。
collection:可以是数组、标准库容器或其他可迭代的数据结构。
以下是一些使用范围基于的 for 循环的示例:
#include <iostream>
#include <vector> //遍历数组
int main01() { int arr[] = {1, 2, 3, 4, 5}; for (int x : arr) { std::cout << x << " "; } return 0;
}
//遍历容器
int main02() { std::vector<std::string> fruits = {"Apple", "Banana", "Cherry"}; for (const std::string& fruit : fruits) { std::cout << fruit << std::endl; } return 0;
}//修改元素
int main03() { std::vector<int> nums = {1, 2, 3, 4, 5}; for (int& n : nums) { // 使用引用以修改元素 n *= 2; // 将每个元素乘以 2 } for (const int& n : nums) { // 输出修改后的元素 std::cout << n << " "; } return 0;
}
类型推导:使用
auto
可以简化类型声明,尤其是在复杂类型或模板类型中。常量引用:如果不需要修改元素,应该使用常量引用(
const T&
)来避免不必要的拷贝,并提高性能。支持的类型:范围 for 循环可以用于支持迭代的任何类型,包括数组、标准库容器及其他实现了
begin()
和end()
的自定义类。
7.string数值转换函数
C++11引入了一些用于字符串与数值之间相互转换的函数,主要通过
<string>
头文件中的std::to_string
和std::stoi
、std::stol
、std::stoll
、std::stof
、std::stod
、std::stold
等函数来实现。
std::to_string
std::to_string
函数用于将数值(如整型、浮点型等)转换为字符串
int num = 42;
double pi = 3.14159; string numStr = to_string(num);
string piStr = to_string(pi);
字符串到数值的转换
-
整数转换
std::stoi
: 转换为int
std::stol
: 转换为long
std::stoll
: 转换为long long
-
浮点数转换
std::stof
: 转换为float
std::stod
: 转换为double
std::stold
: 转换为long double
#include <iostream>
#include <string>
using namespace std;int main() { string intStr = "12345"; string invalidStr = "123abc"; int num = stoi(intStr); // 转换为 int cout << "Converted integer: " << num << endl; // 输出: 12345 try { int invalidNum = stoi(invalidStr); // 尝试转换无效字符串 } catch (const invalid_argument& e) { cout << "Invalid argument: " << e.what() << endl; // 输出异常信息 } string doubleStr = "3.14159"; double pi = stod(doubleStr); // 转换为 double cout << "Converted double: " << pi << endl; // 输出: 3.14159 return 0;
}
这些转换函数在遇到无效输入时会抛出异常。可以使用 std::invalid_argument
和 std::out_of_range
来捕获这些异常:
std::invalid_argument
:表示输入字符串并不代表任何有效的数值。std::out_of_range
:表示转换结果超出了目标类型的范围。
感谢大家!