(个人学习笔记,参考了爱编程的大丙的资料,仅供学习参考)
1.字符串原始字面量:
在C++11中添加了定义原始字符串的字面量,定义方式为: R“xx(原始字符串)xxx”
其中()两边的字符串可以省略。原始字面量R可以直接表示字符串的实际含义,而不需要额外对字符串做转译或连接等操作。
例子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1 = "D:\\hello world";
string s2 = R"(D:\\hello world)";
cout << s1 << endl;
cout << s2 << endl;
return 0;
}
结果:
2.constexpr:
(1)为什么使用:C++98 中 const 具备只读和常量双重语义。
(2)作用:① 区分只读和常量两个语义
② 将没有必要在运行时计算的变量提前到编译期
(3)使用:凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。
const int i=520; // 是一个常量表达式
const int j=i+1; // 是一个常量表达式
constexpr int i=520; // 是一个常量表达式
constexpr int j=i+1; // 是一个常量表达式
对于 C++ 内置类型的数据,可以直接用 constexpr 修饰,但如果是自定义的数据类型(用 struct 或者 class 实现),直接用 constexpr 修饰是不行的。为了提高C++程序的执行效率,我们可以将程序中值不需要发生变化的变量定义为常量,也可以使用 constexpr修饰函数的返回值,这种函数被称作常量表达式函数,这些函数主要包括以下几种:普通函数/类成员函数、类的构造函数 、模板函数。
(4)修饰函数:
<1>普通函数:
① 函数必须要有返回值,并且return 返回的表达式必须是常量表达式。
② 函数在使用之前,必须有对应的定义语句。
③ 整个函数的函数体中,不能出现非常量表达式之外的语句
(using 指令、typedef 语句以及 static_assert 断言return语句除外)。
// error
constexpr int func1()
{
constexpr int a = 100;
constexpr int b = 10;
for (int i = 0; i < b; ++i)
{
cout << "i: " << i << endl;
}
return a + b;
}
// ok
constexpr int func2()
{
using mytype = int;
constexpr mytype a = 100;
constexpr mytype b = 10;
constexpr mytype c = a * b;
return c - (a + b);
}
<2>模板函数:
C++11 语法中,constexpr 可以修饰函数模板,但由于模板中类型的不确定性,因此函数模板实例化后的模板函数是否符合常量表达式函数的要求也是不确定的。如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。
#include <iostream>
using namespace std;
struct Person {
const char* name;
int age;
};
// 定义函数模板
template<typename T>
constexpr T dispaly(T t) {
return t;
}
int main()
{
struct Person p { "喜羊羊", 19 };
//普通函数
struct Person ret = dispaly(p);
cout << "喜羊羊的 name: " << ret.name << ", age: " << ret.age << endl;
//常量表达式函数
constexpr int ret1 = dispaly(250);
cout << ret1 << endl;
constexpr struct Person p1 { "喜羊羊", 19 };
constexpr struct Person p2 = dispaly(p1);
cout << "喜羊羊的 name: " << p2.name << ", age: " << p2.age << endl;
return 0;
}
在上面示例程序中定义了一个函数模板 display(),但由于其返回值类型未定,因此在实例化之前无法判断其是否符合常量表达式函数的要求:
struct Person ret = dispaly(p);由于参数p是变量,所以实例化后的函数不是常量表达式函数,此时 constexpr 是无效的
constexpr int ret1 = dispaly(250);参数是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
constexpr struct Person p2 = dispaly(p1);参数是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
<3> 构造函数:
如果想用直接得到一个常量对象,也可以使用constexpr修饰一个构造函数,这样就可以得到一个常量构造函数了。常量构造函数有一个要求:构造函数的函数体必须为空,并且必须采用初始化列表的方式为各个成员赋值。
#include <iostream>
using namespace std;
struct Person
{
constexpr Person(const char* p, int age) :name(p), age(age){ }
const char* name;
int age;
};
int main()
{
constexpr struct Person p1("美羊羊", 18);
cout << "美羊羊的 name: " << p1.name << ", age: " << p1.age << endl;
return 0;
}
3. nullptr:
C++程序开发中,为了提高程序的健壮性,一般会在定义指针的同时完成初始化操作, 或者在指针的指向尚未明确的情况下,都会给指针初始化为NULL,避免产生野指针。
C++98/03 标准中,将一个指针初始化为空指针的方式有 2 种:
char *ptr = 0;
char *ptr = NULL;
如果源码是C++程序NULL就是0,如果是C程序NULL表示(void*)0。那么为什么要这样做呢? 是由于 C++ 中,void * 类型无法隐式转换为其他类型的指针,此时使用 0 代替 ((void *)0),用于解决空指针的问题。 这个0(0x0000 0000)表示的就是虚拟地址空间中的0地址,这块地址是只读的。
C++ 中将 NULL 定义为字面常量 0,并不能保证在所有场景下都能很好的工作,比如,函数重载时,NULL 和 0 无法区分。
出于兼容性的考虑,C++11 标准并没有对 NULL 的宏定义做任何修改,而是另其炉灶,引入了一个新的关键字nullptr。 nullptr 专用于初始化空类型指针,不同类型的指针变量都可以使用 nullptr 来初始化:
int* ptr1 = nullptr;
char* ptr2 = nullptr;
double* ptr3 = nullptr;
nullptr 无法隐式转换为整形,但是可以隐式匹配指针类型。在 C++11 标准下,相比 NULL 和 0,使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。
4.自动类型推导:
(1)auto:
C++11中auto并不代表一种实际的数据类型,只是一个类型声明的 “占位符”,auto并不是万能的在任意场景下都能够推导出变量的实际类型,使用auto声明的变量必须要进行初始化,以让编译器推导出它的实际类型,在编译时将auto占位符替换为真正的类型。使用语法如下:
auto 变量名 = 变量值;
auto x = 3.14; // x 是浮点型 double
auto y = 520; // y 是整形 int
auto z = 'a'; // z 是字符型 char
auto nb; // error,变量必须要初始化
auto double nbl; // 语法错误, 不能修改数据类型
(2)decltype:
在某些情况下,不需要或者不能定义变量,但是希望得到某种类型,这时候就可以使用C++11提供的decltype关键字了,它的作用是在编译器编译的时候推导出一个表达式的类型,语法格式如下:
decltype (表达式)
int a = 10;
decltype(a) b = 99; // b -> int
decltype(a+3.14) c = 52.13; // c -> double
decltype(a+b*c) d = 520.1314; // d -> double