C++11是新时代的C++亦称为modern C++,是对C++98扩展。C++11旨在手写简便与提高效率。C++11实现了more concise ,more effective。(更简洁,更高效)。
前面的C++文章中也部分涉及了C++11,这里单独拿出来总结一下。这里往下都是C++11的新特新,有必要掌握一下
More concise
1、nullptr
nullptr 是用于解决 NULL 和 0 的有疑义关系的。 NULL 通常被义为(void*)0。但是在一些应用中会引发歧义,所以才会引入nullptr。
#include <iostream>
using namespace std;
void f(int){}
void f(bool){}
void f(void*){}
int main()
{
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls f(int). Never calls f(void*)
f(nullptr); // calls f(void*) overload
}
2、override
override 是辅助你检查是否继承了想要虚继承的函数。关键字 override 则指明, 此种覆写关系, 若此关系不成立, 则以报错的形式提示给用户。
3、final
关键字 final 有两个用途。 第一, 它阻止了从类继承; 第二, 阻止一个虚函数的覆写。
4、=default =delete
C++ 的类有四类特殊成员函数, 它们分别是: 默认构造函数、 析构函数、 拷贝构造函数以及拷贝赋值运算符,这些类的特殊成员函数负责创建、 初始化、 销毁, 或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数, 而又需要用到该特殊成员函数时, 则编译器会隐式的为这个类生成一个默认的特殊成员函数。
#include <iostream>
using namespace std;
class A
{
public:
A() = default;//设置构造器为默认
A(int x ):_x(x)
{}
private:
int _x;
};
int main()
{
A a;
return 0;
}
为了能够让程序员显式的禁用某个函数, C++11 标准引入了一个新特性: "=delete"函数。 程序员只需在函数声明后上“=delete;”, 就可将该函数禁用。
class Singleton
{
public:
static Singleton* getInstance()
{
if(_ins == nullptr)
_ins = new Singleton;
return _ins;
}
Singleton(Singleton &) = delete;//拷贝构造器设置为禁用
Singleton& operator=(Singleton &) = delete;
private:
Singleton(){}
static Singleton* _ins;
};
Singleton* Singleton::_ins = nullptr;
int main()
{
Singleton *ps = Singleton::getInstance();
Singleton ps(*ps);
*ps = Singleton::getInstance();
return 0;
}
5、Raw String Literals
C/C++中提供了字符串, 字符串的转义序列, 给输出带来了很多不变, 如果需要原生义的时候, 需要反转义, 比较麻烦。C++提供了 R"()", 原生字符串, 即字符串中无转义, 亦无需再反义。 但是注意()中的)"会导至提前结束。
#include <iostream>
using namespace std;
string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754";
string path2= "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754";
string path3= R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)";
string path4= R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)";//这个才是对的
int main(int argc, char *argv[])
{
cout<<path<<endl;
cout<<path2<<endl;
cout<<path3<<endl;
cout<<path4<<endl;
return 0;
}
6、Range-Based for
这个的for循环和之前的for看起来似乎一点都不一样,这里的for是基于区间的for循环,比如vector,他是有begin和end的,就在这个begin和end的区间上进行循环。所以说下面注释的那些行是不能用这种for循环的,但是数组arr是可以的,因为arr底层其实也被搞成有begin和end的,这就是原因所在.
#include <iostream>
using namespace std;
int main()
{
//char *p = "aksdfjlaskdf";
//for(auto &i: p)
//{
// cout<<i<<endl;
//}
int arr[10] = {1,2,3,4,5,6,7};
for(auto &i: arr) //使用引用可以改变数组中的内容
{
cout<<i<<endl;
}
string str = "china";
for(auto& ch: str)
{
cout<<ch<<endl;
}
return 0;
}
7、初始化列表 initializar list {}
这个初始化列表其实在前面的STL中使用过。这个就是那个,这是C++11的新特性
#include <iostream>
#include <list>
#include <vector>
#include <map>
using namespace std;
int main(int argc, char *argv[])
{
vector<int> vi = {1,2,3,4,5};
list<int> li = {1,2,3,4,5};
map<int,string> mis =
{
{1,"c"}, {2,"c++"},
{3,"java"},{4,"scala"},{5,"python"}
};
mis.insert({6,"ruby"});
for(auto &is: mis)
{
cout<<is.first<<is.second<<endl;//注意这里用的是.
}
return 0;
}
initializer_list
在 C++11 可以使用 {} 而不是 () 来调用类的构造函数。因为template< class T > class initializer_list; C++11 中提供了新的模板类型initializer_list。initializer_list 对象只能用大括号{}初始化,其内有元素都是 const 的。 常用于构造器和普通函数参数
#include <iostream>
using namespace std;
double sum(const initializer_list<double> &il);
double average(const initializer_list<double> &ril);
int main()
{
auto il = {1,2,3,4,5};
double s = sum({1,2,3,4,5})-
cout<<"sum = "<<s<<endl;
double avg = average({1,2,3,4,5});
cout<<"average = "<<avg<<endl;
return 0;
}
double sum(const initializer_list<double> &il)
{
double s = 0;
for(auto d:il)
s += d;
return s;
}
double average(const initializer_list<double> &ril)
{
double s = 0;
for(auto d:ril)
s += d;
double avg = s/ril.size();//是有size这个函数的
return avg;
}
在构造器中也是一样的使用
8、auto
auto 能够实现类型的自我推导, 并不代表一个实际的类型声明。 auto 只是一个类型声明的占位符。auto 声明的变量, 必须马上初始化, 以让编译器推断出它的实际类型, 并在编译时将auto 占位符替换为真正的类型。注意:auto 不能用于函数参数(C++14 可以通过) 。
9、decltype
decltype获取表达式类型 ,decltype(expr); 所推导出来的类型, 完全与 expr 类型一致。 同 auto 一样, 在编译期
间完成, 并不会真正计算表达式的值 ,关于函数的类型推导, 不可以直接使用函数名字, 可以使用函数的类型生成的对象推导 。
int main()
{
double a = 10;
decltype(a) b;
cout<<sizeof(b)<<endl;
cout<<sizeof(decltype(a))<<endl;
decltype("abcde") ds = "china"; //必须是刚好5个字符,否则报错
cout<<ds<<endl;
char *p = "abc";
decltype(p) pp = "12345";
cout<<sizeof(pp)<<endl;
cout<<pp<<endl;
return 0;
}
组合使用:
推导类型(decltype) 重定义 typedef
对于推导出来的类型进行重定义, 是一种不错的选择。 比宏更准确, 因为发生在编译阶段。
typedef decltype(map<int,string>::value_type()) Int2String;
返回类型推导 auto -> decltype
->decltype()放在函数的结尾, 称为尾推导。 返回值类型用 auto 来配合使用, 是一种绝佳组合。
#include <iostream>
using namespace std;
template<typename T1, typename T2,typename T3>
T1 add(T2 a, T3 b)
{
return a+b;
}
template<typename T1, typename T2>
auto add2(T1 a, T2 b)->decltype(a+b)//注意这里,两数相加可能返回值是T2类型,也可能是T3,这里使用自动推导
{
return a+b;
}
int main(int argc, char *argv[])
{
int a = 1;
float b = 1.1;
auto ret = add<decltype(a+b),int,float>(a,b);
cout<<ret<<endl;
auto ret2 = add2<int,float>(a,b);
cout<<ret2<<endl;
return 0;
}
10、Lambda
简短函数, 就地书写, 调用, 即 Lambda 存在的意义, 常用于取代作回调用的简短函数指针与仿函数。就地书写, 因只有函数体, 即无函数名, 也称匿名函数。
lambda 本质就是匿名的仿函数
格式如下:
[capturelist] (parameterlist) mutable ->return type { function body }
闭包[]与 mutable
lambda 函数能够捕获 lambda 函数外的具有自动存储时期的变量。函数体与这些变量的集合合起来叫闭包。闭包的概念在 lambda 中通过[]来体现出来
#include <iostream>
#include <vector>
#include <list>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <functional>
using namespace std;
bool Compare(int i, int j)
{
return i<j;
}
int main(int argc, char *argv[])
{
vector<int> vi;
srand(time(NULL));
for(int i=0;i <10; i++)
{
vi.push_back(rand()%100);
}
// sort(vi.begin(),vi.end(),Compare);
sort(vi.begin(),vi.end(),[](int x, int y){return x<y;});//sort函数使用Lambda
for_each(vi.begin(),vi.end(),[](int i){cout<<i<<endl;});
return 0;
}
&为引用,=就是复制,本身是const,想改就加mutable。
&和=是全部引进,杀伤力太大,看下面:
[]的捕获规则
形式 | 语意 |
---|---|
[] | 不截取任何变量。 |
[bar] | 仅对外部变量 bar 值传递在函数体中使用。 |
[&bar] | 仅对外部变量 bar 引用传递在函数体中使用。 |
[x, &y] | 仅 x 按值传递, y 按引用传递在函数体中使用。 |
[&] | 截取外部作用域中所有变量, 并作为引用传递在函数体中使用。 |
[=] | 截取外部作用域中所有变量, 并按值传递在函数体中使用。 |
[=, &foo] | 截取外部作用域中所有变量, 并值传递在函数体中使用, 但是对 foo 变量使用引用传递。 |
[&, =foo] | 截取外部作用域中所有变量, 在函数体中作引用传递使用, 但是对 foo 变量作值传递。 =foo -> foo |
上述, 中涉及到值传递要发生拷贝行为, 而引用传递则不会发生拷贝行为。 捕获列表中不允许重复。 比如: [=, a] [&,&this]。
11、enum
前面有说过enum在C语言与C++中的区别,这里介绍一下C++11的新特性
#include <iostream>
using namespace std;
class Painter
{
public:
enum Color { red, green, yellow};
public:
Painter(){}
void dis(Color c)
{
cout<<c<<endl;
}
};
int main()
{
// Painter p;
// p.dis(red);
Painter p;
p.dis(Painter::red);
return 0;
}
#include <iostream>
using namespace std;
enum class Color { red, green, yellow};
class Painter
{
public:
Painter(){}
void dis(Color c)
{
cout<<static_cast<int>(c)<<endl;
}
};
int main()
{
Painter p;
// p.dis(red);
p.dis(Color::red);
return 0;
}
C++11之前我们像上面来使用,在C++11我们还可以这样用指定enum中的类型,我们知道enum默认为int类型,我们可以改成char型,应该是只能改成整形相关的。
#include <iostream>
using namespace std;
enum class color:int { red, green, yellow};
enum class colorX:char { red, green, yellow };
int main()
{
//使用域运算符访问枚举体成员, 强转后打印
std::cout << static_cast<int>(color::red) << std::endl;
std::cout << static_cast<int>(colorX::red) << std::endl;
return 0;
}
版权属于:孟超
本文链接:https://mengchao.xyz/index.php/archives/504/
转载时须注明出处及本声明
非常有用