C++11新特性
1.原始字面量
- 解决字符串的歧义问题,比如转义字符,我们可以使用原始字面量
//在Windows中(Linux是斜杠,所以不需要转义),我们需要对路径的字符串中反斜杠转义
//不转义
string s = "D:\hello\world\test.txt";
cout<<s<<endl;
//转义
s = "D:\\hello\\world\\test.txt";
cout<<s<<endl;
//原始字面量,其中xxx可以是任意字符串,作用是用来描述的,打印时不会显示,但左右两边必须相同
s = R"xxx(D:\hello\world\test.txt)xxx";
cout<<s<<endl;
对于较长的字符串,我们可以换行显示,但是需要使用反斜杠\连接(python中也可这样做),也可以不用\连接,使用原始字面量亦可,可以提高可读性
string s = "<html>\
<head>\
<title>\
火影\
</title>\
</head>\
<body>
</html>";
cout<<s<<endl;
s = R"(<html>
<head>
<title>
火影
</title>
</head>
<body>
</html>)";
cout<<s<<endl;
2.指针空值类型 - nullptr
nullptr用于初始话空指针,之前版本的CPP使用NULL初始化空指针,可以看到在CPP中NULL被定义为0,而在C语言中定义为((void*)0).
这是因为在CPP中,语言对于类型的要求更加的严格了,CPP不允许(void*)的变量隐式转换为其他类型
//这样才是正确的
int *ptr5 = (int*)ptr4;
//nullptr会自动转换为对应的变量类型,对于ptr3,nullptr会是int*类型,而ptr2,nullptr会是void*类型
int * ptr3 = nullptr;
void *ptr2 = nullptr;
有一点值得注意,nullptr无法转化为整形,但可以隐式转化匹配指针类型,相比于NULL与0,更加的健壮。
比如对于如下例子,NULL就不会调用形参为指针的函数。
void func(char *p);
void func(int p);
3.constexpr
http://c.biancheng.net/view/7807.html http://c.biancheng.net/view/7781.html https://www.bilibili.com/video/BV1vE411p758?from=search&seid=3674808646488637514&spm_id_from=333.337.0.0
我们可以通过CPP中的const设置常量,但是const有两种语义:变量只读
,修饰常量
,但是其实const“常量”可以修改,const表达的更多的是readonly。#define
修饰的常量是预编译期的常量,const
修饰的常量是运行时常量,而constexpr
表示的是编译期常量。修改const变量比如:
#include <iostream>
using namespace std;
int main()
{
int a = 10;
const int & con_b = a;
cout << con_b << endl;
a = 20;
cout << con_b << endl;
}
constexpr用来修饰常量表达式,常量表达式指的是又多个(>1)常量(值不会变)组成并且在编译过程中就得到运算结果的表达式。
非常量表达式只能在程序运行阶段计算出结果,但是常量表达式只需要在编译期间计算一次,可以提高计算效率
在定义常量时,const
和constexpr
是等价,都可以在程序的编译阶段计算出结果
,比如:
const int m = f(); // 不是常量表达式,m的值只有在运行时才会获取。
const int i=520; // 是一个常量表达式
const int j=i+1; // 是一个常量表达式
constexpr int i=520; // 是一个常量表达式
constexpr int j=i+1; // 是一个常量表达式
对于C++内置类型的数据,可以直接用constexpr
修饰,但如果是自定义的数据类型(用struct
或者class
实现),直接用constexpr
修饰不行。
// 此处的constexpr修饰是无效的
constexpr struct Test
{
int id;
int num;
};
4.常量表达式函数
可以使用constexpr
修饰函数的返回值,这种函数被称作常量表达式函数
,这些函数的主要包括:普通函数/类成员函数
,类的构造函数
,模板函数
。这样一来可以提高C++的程序的效率。
修饰函数
constexpr
修饰函数使其成为常量表达式函数,需要满足以下几个条件:
- 函数最好有返回值,并且return返回的表达式必须式常量表达式
// error,不是常量表达式函数
constexpr void func1()
{
int a = 100;
cout << "a: " << a << endl;
}
// error,不是常量表达式函数,返回值不是常量表达式
constexpr int func1()
{
int a = 100;
return a;
}
- 函数在使用之前,必须有对应的定义语句。
#include <iostream>
using namespace std;
constexpr int func1();
int main()
{
constexpr int num = func1(); // error
return 0;
}
constexpr int func1()
{
constexpr int a = 100;
return a;
}
在测试程序 constexpr int num = func1(); 中,还没有定义 func1() 就直接调用了,应该将 func1() 函数的定义放到 main() 函数的上边。
- 整个函数的函数体中,不能出现非常量表达式之外的语句(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;//using与typedef类似,只不过using更加的直观
constexpr mytype a = 100;
constexpr mytype b = 10;
constexpr mytype c = a * b;
return c - (a + b);
}
因为 func1() 是一个常量表达式函数,在函数体内部是不允许出现非常量表达式以外的操作,因此函数体内部的 for 循环是一个非法操作。
以上三条规则不仅对应普通函数适用,对应类的成员函数也是适用的:
class Test
{
public:
constexpr int func()
{
constexpr int var = 100;
return 5 * var;
}
};
int main()
{
Test t;
constexpr int num = t.func();
cout << "num: " << num << endl;
return 0;
}
修饰模板函数
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 { "luffy", 19 };
//普通函数
struct Person ret = dispaly(p);
cout << "luffy's name: " << ret.name << ", age: " << ret.age << endl;
//常量表达式函数
constexpr int ret1 = dispaly(250);
cout << ret1 << endl;
constexpr struct Person p1 { "luffy", 19 };
constexpr struct Person p2 = dispaly(p1);
cout << "luffy's 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 是有效的
修饰构造函数
如果想用直接得到一个常量对象,也可以使用 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("luffy", 19);
cout << "luffy's name: " << p1.name << ", age: " << p1.age << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)