C++11新特性
C++11新特性
- 在
C++03标准中,__cplusplus的值被预定为$199711L$,在C++11标准中,__cplusplus的值被预定为$201103L$,程序判断是否支持C++11(有些编译器是部分支持):
1 |
- 尽量用
assert(n>0)进行断言,程序发布时加上宏定义值NDEBUG把断言禁掉,调试的时候去掉NDEBUG宏 lambda函数语法定义:(参数列表和返回类型都是可选的部分,捕捉列表和函数体都可能为空。最简略的lambda函数为:[]{};)
1 | [capture](parameters)mutable->return-type{statement} |
[capture]:捕捉列表,有以下几种:
[var]表示值传递方式捕捉变量var
[=]表示值传递方式捕捉所有父作用域的变量(包括this)
[&var]表示引用传递捕捉变量var
[&]表示引用传递捕捉所有父作用域的变量(包括this)
[this]表示值传递方式捕捉当前的this指针 值传递与引用传递的区别:按值方式传递的捕捉列表,其传递的值在
lambda函数定义的时候就已经决定了。按引用传递的捕捉列表变量,其传递的值等于lambda函数调用时的值,举例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main()
{
int j=12;
auto by_val_lambda=[=]{return j+1;};
auto by_ref_lambda=[&]{return j+1;};
cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
j++;
cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
}运行结果:
1
2
3
4by_val_lambda:13
by_ref_lambda:13
by_val_lambda:13
by_ref_lambda:14(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同()一起省略
mutable:修饰符。默认情况下,
lambda函数总是一个const函数,mutable可以取消常数特性。在使用该修饰符时,参数列表不可省略(即使参数为空)
->return-type:返回类型。不需要返回值的时候可以连同符号
->一起省略。在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕捉的变量。
继承
关键字
final:阻止继承,声明时使用的话,子类无法再重栽该虚函数;关键字
override:重栽父类的虚函数时,在声明函数时进行修饰,可以在编译时检查函数名和参数列表;
1
2
3
4
5
6
7
8
9
10
11
12
13class Object{
virtual void fun()=0;
virtual void output()=0;
};
class Base:public Object{
void fun() final; // 声明为final
void output()override; //如果名字写错或者参数写错,编译出错,主要用来明确多态
};
class Derived:public Base{
void fun(); // 无法通过编译
};C++11支持using关键字,可以直接使用基类的构造函数,之前using只能使用基类的普通函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Base {
public:
Base(int i) {cout<<"Base construct i:"<<i<<endl;}
Base(int i,int j) { cout << "Base construct i:" << i<<" j:"<<j << endl; }
virtual ~Base() {}
void f(double i) { cout << "Base:" << i << endl; }
};
class Derived :public Base{
public:
using Base::Base;
using Base::f;
void f(int i,int j) { cout << "Derived:" << i << endl; }
};
int main()
{
Base b;
b.f(4.5);
Derived d1(5),d2(5,6);//这里自动调用基类的构造函数
d1.f(4.5);//如果没有using base::f会产生编译错误
d1.f(5, 6);
}输出结果为:
1
2
3
4Base construct i:5
Base construct i:5 j:6
Base:4.5
Derived:5移动构造函数
移动构造函数接受一个所谓的“右值引用”的参数(右值可以理解为临时变量的引用),
然后将临时变量的数据指针转移给本对象成员,随后将临时变量的数据指针置空。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42class HasPtrMem
{
public:
HasPtrMem() :d(new int(0)) {
cout << "Construct:" << ++n_cstr << endl;
}
HasPtrMem(const HasPtrMem& h) :d(new int(*h.d)) {
cout << "Copy construct:" << ++n_cptr << endl;
}
HasPtrMem(HasPtrMem&& h) :d(h.d) { //移动构造函数
h.d = nullptr; //将临时值的指针成员置空
cout << "Move construct:" << ++n_mvtr << endl;
}
~HasPtrMem() {
delete d;
cout << "Destruct:" << ++n_dstr << endl;
}
public:
int* d;
static int n_cstr;
static int n_dstr;
static int n_cptr;
static int n_mvtr;
};
int HasPtrMem::n_cstr = 0;
int HasPtrMem::n_dstr = 0;
int HasPtrMem::n_cptr = 0;
int HasPtrMem::n_mvtr = 0;
HasPtrMem GetTemp() {
HasPtrMem h;
cout << "Resource from " << __func__ << ":" << hex << h.d << endl;
return h;
}
int main()
{
HasPtrMem a = GetTemp();
cout << "Resource from " << __func__ << ":" << hex << a.d << endl;
}输出结果:
1
2
3
4
5
6
7
8Construct:1 //函数GetTemp()中变量h构造函数
Resource from GetTemp:0x603010 //函数GetTemp()第二句
Move construct:1 //函数GetTemp()第三句返回时会产生临时对象,调用移动构造函数(如果没有移动构造函数,将会调用拷贝构造函数,开销比较大)
Destruct:1 //函数GetTemp()中h变量析构
Move construct:2 //临时变量给main()函数中的a赋值,调用移动构造函数,同上
Destruct:2 //临时变量析构
Resource from main:0x603010 //调用main()函数中第二句
Destruct:3 //main()函数中变量a析构auto作为类型指示符,以下几种情况不能用:1
2
3
4
5
6
7
8
9
10
11
12
13
14void fun(auto x=1){} //1、auto函数参数,无法通过编译
struct str{
auto var=10; //2、auto非静态成员变量,无法通过编译
};
int main(){
char x[3];
auto y=x;
auto z[3]=x; //3、auto数组,无法通过编译
//4、auto模板参数(实例化时),无法通过编译
vector<auto> v={1};
}(1)对于函数
fun来说,auto不能是其形参类型。如果需要泛型的参数,需要用使用模板;(2)非静态成员变量的类型不能是
auto;即使结构体的成员有初始值,编译器也不会对其进行推导,;(3)编译器禁止声明
auto数组;(4)实例化模板不能使用
auto作为模板参数;以上四种情况基本相似,虽然看起来很容易能推导出
auto对应的类型,但现在的C++11还不支持这样的使用方式。decltype的应用
1
2
3
4
5
6
7
8
9
10int main(){
vector<int> vec;
typedef decltype(vec.begin()) vectype;
for(vectype i=vec.begin();i<vec.end();i++){
//dosomething
}
for(decltype(vec)::iterator i=vec.begin();i<vec.end();i++){
//dosomething
}
}遍历容器
1
2
3
4
5
6
7
8int main(){
{
int arr[5]={1,2,3,4,5};
for(int& e:arr) //需要修改项用引用,这里int可用auto代替
e*=2;
for(int e:arr) //不需要修改项直接用变量,这里int可用auto代替
cout<<e<<'\t';
}for_each与transform1
2
3
4
5
6
7
8
9
10
11
12///将容器a中的元素求log后放入a中
std::transform(a.begin(),a.end(),a.begin(),[](double d)->double{return log(d);});
std::for_each(a.begin(),a.end(),[](double& d){d = log(d);});
///两个vector<double>V,W要进行某个运算,如计算U = sin(V) + W,结果存入U;
std::transform(v.begin(),v.end (),w.begin (),u.begin (),[&](double a ,double b)->double{
return sin(a)+b;
});
///使用std::transform可以比较方便的实现序列容器的四则运算
stl提供了plus,multiplies,divides,modulus,negate等函数对象,方便实现加减乘除等运算
std::transform(a.begin(),a.end(),b.begin(),std::back_inserter(c),std::plus<double>());原子类型(多线程时不用加锁)
使用atomic类模板自定义类型:
std::atomic<T> t;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
atomic<int> a;
atomic<int> b;
int Thread1(int) {
int t = 1;
a.store(t, memory_order_relaxed);
b.store(2, memory_order_release);// 本原子操作前所有的写原子操作必须完成
return 0;
}
int Thread2(int) {
while (b.load(memory_order_acq_rel) != 2); // 本原子操作必须完成才能执行之后所有的读原子操作
cout << a.load(memory_order_relaxed) << endl;
return 0;
}
int main()
{
thread t1(Thread1, 0);
thread t2(Thread2, 0);
t1.join();
t2.join();
return 0;
}default与delete1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class Base{
public:
Base(int i){}
Base()=default; ///显式声明系统提供的缺省构造函数
Base(const Base& rb)=delete; ///显式删除系统提供的缺省拷贝构造函数
};
bool isLucky(int number);
int main()
{
isLucky('a'); ///存在隐式转换
isLucky(true); ///存在隐式转换
isLucky(3.5); ///存在隐式转换
}
///增加删除不想要的重载函数
bool isLucky(char)=delete;
bool isLucky(bool)=delete;
bool isLucky(double)=delete;
int main()
{
isLucky('a'); ///错误!调用删除函数
isLucky(true); ///错误!
isLucky(3.5); ///错误!
}枚举类型增加作用域限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21int main()
{
enum Color{
black,
white,
red
};
auto white=false; //编译提示变量重定义
}
int main()
{
enum class Color:uint32_t{ //可以指定枚举值得类型,不指定时编译器会自动指定
black,
white,
red
};
auto white=false; ///编译OK
Color c = white; ///编译错误,提示当前作用域没有这个枚举元素
Color c = Color::white; ///编译OK
}智能指针
std::unique_ptr和std::shared_ptr的区别共同之处:是开发人员不再需要关心指针所指内存的释放问题,当这块内存不在被引用时,会被自动释放。
区别:指向同一内存的某一类型指针的数量,
std::unique_ptr只允许一个,而std::shared_ptr允许多个。1
2
3
4
5
6
7
8
9
10
11
12
13std::unique_ptr ///最多允许一个,当这个智能指针被销毁时,对应的内存会被自动释放。
///如果试图去拷贝一个std::unique_ptr,编译器会报错,例如下面的代码就会报错
std::unique_ptr<Base> p(new Base); // OK
std::unique_ptr<Base> p1 = p; // 编译错误,不能拷贝std::unique_ptr
///std::unique_ptr支持move语法,所以可以这样
std::unique_ptr<Base> p(new Base); // OK
std::unique_ptr<Base> p1 = std::move(p); // OK
///对于std::shared_ptr,上面的拷贝的那个例子就OK,
std::shared_ptr<Base> p(new Base); // OK
std::shared_ptr<Base> p1 = p; // OK