c++11的一些特性

C++lambda表达式

匿名函数

C++在C11标准中引入了匿名函数,即没有名字的临时函数,又称之为lambda表达式.lambda表达式 实质上是创建一个匿名函数/对象

基本格式

caputrueopt->ret{body;};

[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}

比如说 {cout << “hello” << endl; };
对于匿名函数,一般用于传函数参数,当然也可以直接定义调用.

1
2
auto pfun=[](){cout<<"hello world"<<endl;}		//这里使用auto自动判断类型  其实是函数指针
pfun(); //调用这个函数

各个部分说明

函数对象参数(不可省略)

[] 不截取任何变量
[&] 截取外部作用域中所有变量,并作为引用在函数体中使用
[=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
[=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
[bar] 截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
[this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。

重载参数列表

调用的时候需要传递的参数

返回类型(可以自动推导 可以省略)

auto fun = [](int x, int y)->int {cout << x + y << endl; return y;};
对于这里的->int 这个int就是返回值类型,如果有返回值类型需要在前面加上->
直接省略写成这种格式也可以[](int x,int y){cout<<x+y<<endl;return y;}

函数体

lambda表达式的函数体中可以和普通函数一样写,可以使用捕获的参数,写这个函数实现的具体步骤.

函数选项opt (可以省略)

可以填mutable,exception,attribute
mutable 表示函数体可以修改捕获变量的,同时可以访问捕获对象的非常属性成员函数
exception说明lambda表达式是否抛出异常以及何种异常
attribute用来声明属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
int main()
{
auto ptr = []() {cout << "hello" << endl; }; //lambda表达式
ptr();//调用函数

auto fun = [](int x, int y)->int {cout << x + y << endl; return y;}; //带参数的lambda表达式
// 这里的->后面写的是返回值类型
auto z=fun(3, 4);
cout << z << endl;

cin.get();
return 0;
}

mutable关键字

在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。

下面是一个小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ClxTest
{
 public:
  void Output() const;
};

void ClxTest::Output() const
{
 cout << "Output for test!" << endl;
}

void OutputTest(const ClxTest& lx)
{
 lx.Output();
}

类ClxTest的成员函数Output是用来输出的,不会修改类的状态,所以被声明为const的。
函数OutputTest也是用来输出的,里面调用了对象lx的Output输出方法,为了防止在函数中调用其他成员函数修改任何成员变量,所以参数也被const修饰。

但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。

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
class ClxTest
{
 public:
  ClxTest();
  ~ClxTest();

  void Output() const;
  int GetOutputTimes() const;

 private:
  mutable int m_iTimes;
};

ClxTest::ClxTest()
{
 m_iTimes = 0;
}

ClxTest::~ClxTest()
{}

void ClxTest::Output() const
{
 cout << "Output for test!" << endl;
 m_iTimes++;
}

int ClxTest::GetOutputTimes() const
{
 return m_iTimes;
}

void OutputTest(const ClxTest& lx)
{
 cout << lx.GetOutputTimes() << endl;
 lx.Output();
 cout << lx.GetOutputTimes() << endl;
}

计数器m_iTimes被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。

Defaulted 和 Deleted 函数

C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。例如:

class X{
private:
int a;
};

X x;
上中,程序员并没有定义类 X 的默认构造函数,但是在创建类 X 的对象 x 的时候,又需要用到类 X 的默认构造函数,此时,编译器会隐式的为类 X 生成一个默认构造函数。该自动生成的默认构造函数没有参数,包含一个空的函数体,即 X::X(){ }。虽然自动生成的默认构造函数仅有一个空函数体,但是它仍可用来成功创建类 X 的对象 x,清单 1 也可以编译通过。

但是,如果程序员为类 X 显式的自定义了非默认构造函数,却没有定义默认构造函数的时候,清单 2 将会出现编译错误:

清单 2
class X{
public:
X(int i){
a = i;
}
private:
int a;
};

X x; // 错误 , 默认构造函数 X::X() 不存在
清单 2 编译出错的原因在于类 X 已经有了用户自定义的构造函数,所以编译器将不再会为它隐式的生成默认构造函数。如果需要用到默认构造函数来创建类的对象时,程序员必须自己显式的定义默认构造函数。例如:

清单 3

class X{
public:
X(){}; // 手动定义默认构造函数
X(int i){
a = i;
}
private:
int a;
};

X x; // 正确,默认构造函数 X::X() 存在
从清单 3 可以看出,原本期望编译器自动生成的默认构造函数需要程序员手动编写了,即程序员的工作量加大了。此外,手动编写的默认构造函数的代码执行效率比编译器自动生成的默认构造函数低。类的其它几类特殊成员函数也和默认构造函数一样,当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数,而需要程序员手动编写,加大了程序员的工作量。类似的,手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。

Defaulted 函数的提出
为了解决如清单 3 所示的两个问题:1. 减轻程序员的编程工作量;2. 获得编译器自动生成的默认特殊成员函数的高的代码执行效率,C++11 标准引入了一个新特性:defaulted 函数。程序员只需在函数声明后加上“=default;”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体。例如:

清单 4
class X{
public:
X()= default;
X(int i){
a = i;
}
private:
int a;
};
X x;
在清单 4 中,编译器会自动生成默认构造函数 X::X(){},该函数可以比用户自己定义的默认构造函数获得更高的代码效率。

背景问题
对于 C++ 的类,如果程序员没有为其定义特殊成员函数,那么在需要用到某个特殊成员函数的时候,编译器会隐式的自动生成一个默认的特殊成员函数,比如拷贝构造函数,或者拷贝赋值操作符。例如:

清单 5
class X{
public:
X();
};

int main(){
X x1;
X x2=x1; // 正确,调用编译器隐式生成的默认拷贝构造函数
X x3;
x3=x1; // 正确,调用编译器隐式生成的默认拷贝赋值操作符
}
在清单 5 中,程序员不需要自己手动编写拷贝构造函数以及拷贝赋值操作符,依靠编译器自动生成的默认拷贝构造函数以及拷贝赋值操作符就可以实现类对象的拷贝和赋值。这在某些情况下是非常方便省事的,但是在某些情况下,假设我们不允许发生类对象之间的拷贝和赋值,可是又无法阻止编译器隐式自动生成默认的拷贝构造函数以及拷贝赋值操作符,那这就成为一个问题了。

Deleted 函数的提出
为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性:deleted 函数。程序员只需在函数声明后加上“=delete;”,就可将该函数禁用。例如,我们可以将类 X 的拷贝构造函数以及拷贝赋值操作符声明为 deleted 函数,就可以禁止类 X 对象之间的拷贝和赋值。

清单 6
class X{
public:
X();
X(const X&) = delete; // 声明拷贝构造函数为 deleted 函数
X& operator = (const X &) = delete; // 声明拷贝赋值操作符为 deleted 函数
};

int main(){
X x1;
X x2=x1; // 错误,拷贝构造函数被禁用
X x3;
x3=x1; // 错误,拷贝赋值操作符被禁用
}
在清单 16 中,虽然只显式的禁用了一个拷贝构造函数和一个拷贝赋值操作符,但是由于编译器检测到类 X 存在用户自定义的拷贝构造函数和拷贝赋值操作符的声明,所以不会再隐式的生成其它参数类型的拷贝构造函数或拷贝赋值操作符,也就相当于类 X 没有任何拷贝构造函数和拷贝赋值操作符,所以对象间的拷贝和赋值被完全禁止了。

全局变量extern

def.h中声明extern int m;
def.cpp中int m;
两个文件必须要有
在主程序中添加头文件,include “def.h”后,就可以直接使用m变量。

静态全局变量

static:

用static修饰的变量,在其所限定的作用域中只会有一分内存

1:在一个函数内部:说明是一个静态局部变量,

不管这个函数被调用多少次,这个static修饰的变量只会有一分内存,也就是说当这个变量多次被修改,都是在上一次基础上修改,不会从头再来

2:在一个文件内部,函数外部:静态全局变量

该文件中的函数都可以访问到,并且不同函数在对该变量修改时都是在上一个函数修改的基础上修改的

静态全局变量和非静态全局变量的区别

static 限制了变量的作用域只在该文件里,所以加上static在别的文件中定义一个相同的static没有问题
没有static修饰的全局变量,要是在不同文件中定义了相同的变量名,程序会报错

静态全局变量声明:在头文件中!
静态全局变量定义:在.cpp文件中,与函数同级。定义前一定要加类名。

1
2
3
4
5
6
*.h
class test
static int word;

*.cpp
int class::word=1