C++ 11 常用特性(一)
1. 原始字面量
在C++11 中添加了定义原始字符串的字面量,定义方式为:
R"xxx(原始字符串)xxx" 其中()两边的字符串可以省略。原始字面量R可以直接表示字符串的实际含义,而不需要额外对字符串做转译或链接等操作。
void rawliteral()
{
string s1 = "D:\hello\world\test.Text" ;
cout<<s1<<endl;
string str = R"(D:\hello\world\test.Text)";
cout<<str<<endl;
}
2. nullptr
? 在C++ 程序开发中,为了提高程序的健壮性,一般会在定义指针的同时完成初始化操作,或者在指针的指向尚未明确的情况下,都会给指针初始化为NULL ,避免产生野指针(没有明确指向的指针,操作这种指针极可能导致程序发生异常)。nullptr 专用于初始化空类型指针,不同类型的指针变量都可以使用nullptr 来初始化:
int main()
{
int *ptr1 = nullptr;
char *ptr2 = nullptr;
double *ptr3 = nullptr;
void *ptr5 = nullptr;
}
3. constexpr 常量表达式修饰符
? C++ 程序从编写完毕到执行分为四个阶段:预处理、编译、汇编、链接 4个阶段,得到可执行程序之后就可以运行了。需要强调的是,常量表达式和非常量表达式的计算实际不同,非常量表达式只能在程序运行阶段计算出结果,但是常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率,因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。 ? C++11 中添加了 constexpr 关键字用来表达常量表达式,在使用中,建议将const 和constexpr 的功能区分开,即凡是表达“只读”语义的场景都是用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;
//同时需要注意,constexpr只能修饰C++内置类型的数据,不能修饰自定义数据类型,类似(class,struct)
常量表达式函数
为了提高C++ 程序的执行效率,我们可以将程序中值不需要发生变化的变量定义为常量,也可以使用constexpr 修饰函数的返回值,这种函数称为常量表达式函数,这些函数主要包括以下几种:普通函数\类成员函数、类构造函数、模板函数 。
constexpr 修饰函数,必须要满足以下几个条件:
- 函数必须要有返回值,并且
return 返回的表达式必须是常量表达式:
// error,没有返回值,不是常量表达式函数
constexpr void func1()
{
int a = 100;
cout << "a: " << a << endl;
}
// error,返回值不是常量表达式,不是常量表达式函数
constexpr int func1()
{
int a = 100;
return a;
}
2. 函数在使用之前,必须有对应的定义语句
constexpr int func1();
int main()
{
constexpr int num = func1(); // error
return 0;
}
constexpr int func1()
{
constexpr int a = 100;
return a;
}
- 整个函数的函数体中,不能出现非常量表达式之外的语句(
using\typedef\static_asset断言、return语句除外 )
// error for循环只有在程序运行之后才能得到i的值,所以不满足constexpr修饰函数的条件
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);
}
struct Person {
const char* name;
int age;
};
template<typename T>
constexpr T display(T t) {
return t;
}
void constexprtemplate() {
struct Person p {"zsj",10 };
struct Person ret = display(p);
cout << ret.name << ret.age << endl;
constexpr int ret1 = display(250);
cout << ret1<<endl;
constexpr struct Person p1 { "alj", 9 };
constexpr struct Person p2 = display(p1);
cout << p2.name << p2.age << endl;
}
struct Person ret = dispaly(p); 由于参数p 是变量,所以实例化后的函数不是常量表达式函数,此时 constexpr 是无效的 constexpr int ret1 = dispaly(250); 参数是常量,符合常量表达式函数的要求,此时constexpr 是有效的 constexpr struct Person p2 = dispaly(p1); 参数是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
struct Person {
constexpr Person(const char* p, int age) :name(p), age(age)
{
}
const char* name;
int age;
};
4. 自动类型推导
//语法
auto 变量名 = 变量值;
auto x = 3.14; // x 为浮点值
auto y = 520; //y 为int
auto z = 'a'; // z 为char
auto nb; //error 必须初始化
? 不仅如此,auto 还可以和指针、引用结合起来使用也可以带上 const、volatile 限定符,在不同的场景下有对应的推导规则,规则内容如下:
? 当变量不是指针或者引用类型时,推导的结果中不会保留const、volatile 关键字 ? 当变量是指针或者引用类型时,推导的结果中会保留const、volatile 关键字
void autoTest()
{
int temp = 100;
auto* a = &temp; //a的类型为int* auto 为int
auto b = &temp; //b的类型为int* auto 为 int*
auto& c = temp; //c的类型为int& auto为 int
auto d = temp; //d的类型为int auto为int
const auto a1 = temp; //变量a1的类型为const int,所以auto为int
auto a2 = a1; //变量a2数据类型为int 因为a2没有声明为指针或引用因此const属性被去掉,auto为int
const auto& a3 = temp; // 变量a3的类型为const int&,a3被声明为引用所以const属性保留,auto为int
auto& a4 = a3; // 变量a4数据类型为const int&,a4被声明为引用因此 const 属性被保留,auto 为 const int 类型
}
//语法
decltype(表达式)
int a = 10;
decltype(a) b = 99; // b int
decltype(a+3.14) c = 52.13 // C double
需要注意:表达式是一个左值,或者被括号 ( ) 包围,使用 decltype 推导出的是表达式类型的引用(如果有 const、volatile 限定符不能忽略)
class Test{
public:
int num;
};
int main(){
const Test obj;
//带有括号的表达式
decltype(obj.num) a = 0; //int
decltype((obj.num)) b = a; //括号 int&
//加法表达式
int n = 0, m = 0;
decltype(n + m) c = 0; //int
decltype(n = n + m) d = n; //左值 int&
}
decltype 应用:
template<typename T>
class Container{
public:
void printContainer(T& c){
for(_it=c.begin();_it!=c.end();_it++){
cout<<*_it<<" ";
}
cout<<endl;
}
private:
decltype(T().begin()) _it; //用来推到迭代器类型
};
int main(){
const list<int> lst{1,2,3,4,5,6};
Container<const list<int>> obj;
obj.printContainer();
return 0;
}
#include <iostream>
using namespace std;
// R->返回值类型, T->参数1类型, U->参数2类型
template <typename R, typename T, typename U>
R add(T t, U u)
{
return t + u;
}
int main()
{
int x = 520;
double y = 13.14;
// auto z = add<decltype(x + y), int, double>(x, y);
auto z = add<decltype(x + y)>(x, y); // 简化之后的写法
cout << "z: " << z << endl;
return 0;
}
? 在C++11 增加了返回类型后置语法,说白一点就是将decltype 和auto 结合起来完成返回类型的推导。
//语法格式
auto func(arg1,arg2,...) -> decltype(参数表达式)
通过上述返回类型后置代码的分析,可得到结论:auto 会追踪decltype() 推导出的类型,由此上边的add() 函数可以做如下修改:
template <typename T, typename U>
auto add(T t, U u) -> decltype(t+u)
{
return t + u;
}
5. final 和override
? C++增加了final 关键字来限制某个类不能被继承,或者某个虚函数不能被重写
- 修饰函数
class Base{
public:
virtual void test(){}
};
class child:public Base{
public:
void test()final{}
};
class GrandChild:public child{
public:
// 语法错误,不允许重写
void test(){}
}
- 修饰类
class Base final
{
public:
virtual void test(){}
};
// 语法错误
class Child : public Base
{};
class Base
{
public:
virtual void test()
{
cout << "Base class...";
}
};
class Child : public Base
{
public:
void test() override
{
cout << "Child class...";
}
};
6. 模板的优化
template<typename T>
class Base{
public:
void test(T& c){
auto it = c.begin();
for(;it!=c.end();it++){
cout<<*it<<" ";
}
cout<<endl;
}
};
int main()
{
vector<int> v{1,2,3,4,5,6,7};
Base<vector<int>> b;
b.test();
return 0;
}
如果使用 C++98/03 标准来编译上边的这段代码,就会得到如下的错误提示:
test.cpp:25:20: error: '>>' should be '> >' within a nested template argument list
Base<vector<int>> b;
根据错误提示中描述模板的两个右尖括之间需要添加空格,这样写起来就非常的麻烦,C++11 改进了编译器的解析规则,尽可能地将多个右尖括号(> )解析成模板参数结束符,方便我们编写模板相关的代码。
在C++98/03 标准中,类模板可以有默认的模板参数:
template<typename T=int,typename T t = 520>
class Base{};
int main(){
Test<> t;
}
但是不支持函数的默认模板参数,在C++11 中添加了对函数模板默认参数的支持:
template<typename T = int> //C++98/03不支持这种写法 C++11之后支持
void func(T t){
cout<<t<<endl;
}
int main()
{
func(100);
return 0;
}
|