c++11增加新关键字auto:实现自动类型的推导
使用auto关键字定义的变量必须要有初始化表达式。
#include<iostream>
using namespace std;
class Example
{
private:
int x;
public:
Example(int xx = 0):x(xx){}
int Show()const
{
return x;
}
};
int main()
{
//auto a;
//错误,auto是通过初始化表达式进行类型推导的。
//如果没有初始化表达式,就无法确定a的类型。
auto str = "HelloWorld";
cout<<str<<endl;
//对自定义类型进行类型推导
auto ex = new Example();
cout<<ex->Show()<<endl;
delete ex;
return 0;
}
c++11新增关键字decltype,这个关键字可以从一个变量或表达式中得到类型
#include<iostream>
using namespace std;
int main()
{
double y = 100;
cout<<sizeof(decltype(y))<<endl;//8
decltype(y)x = 200;
cout<<x<<endl;
return 0;
}
c++11引入表示空指针类型的关键字nullptr.
#include<iostream>
using namespace std;
void F(int a)
{
cout<<a+1<<endl;
}
void F(int * p)
{
cout<<p<<endl;
}
int main()
{
int * p = nullptr;
int * q = NULL;
cout<<p<<endl; //0
cout<<q<<endl; //0
//c++11引入关键字nullptr表示空指针类型,为了兼容NULL这个宏
//所以两者的值都为0.但是,NULL可以和0相互替代。而0不可以表
//示为nullptr
bool equal = (p == q);
cout<<equal<<endl; //1
//int a = nullptr; //error,nullptr不可以表示0
int b = NULL; //right,NULL就是0
cout<<b<<endl; //0
F(0); //1
F(nullptr); //0
return 0;
}
基于范围的for循环(亦称区间迭代)
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
int arr[5] {100,200,300,400,500};
for(auto x:arr)
{
cout<<x<<endl;
}
map<int,string>m{{1,"wang ming"},{2,"xiao hua"}};
for(auto p:m)
{
cout<<p.first<<" "<<p.second<<endl;
}
return 0;
}
更加优雅的初始化方法
- 示例1
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int arr[] {100,200,300,400,500};//等于号可以省略
vector<double>ve {100,200,300,400,500};
for(auto x:ve)
{
cout<<x<<" ";
}
return 0;
}
- 示例2
#include <iostream>
#include <initializer_list>
using namespace std;
class Example
{
public:
explicit Example(int x):x(x){}
int GetX()const
{
return x;
}
private:
int x;
};
int main()
{
//c++扩大了用大括号扩起的列表(初始化列表)
//的适用范围,使其可用于所有内置类型和用户
//定义的类型(即类类型)。使用初始化列表时
//可以添加等号也可以不添加。
int a{ 100 };
cout << a << endl;
//也可以用于new表达式中
int * pointer = new int[2]{ 100,200 };
cout << pointer[0] <<" "<<pointer[1]<< endl;
//创建对象时也可以使用大括号(而不是圆括号)
//括起的列表来调用构造函数
Example x{ 100 };
//Example x = {1000};
cout << x.GetX() << endl;
//c++11新增头文件initializer_list,这个模板类的
//成员变量是指针类型
initializer_list<int>arr{ 100,200,300,400 };
cout << *arr.begin() << endl;
}
c++11还引入了变长参数模板。
tuple是一个类模板,允许我们将多个不同类型的成员绑定为单一对象。每一个tuple对象包含指定数量的成员。但对一个给定的tuple对象,标准库并没有限制我们可以定义的成员数量上限。tuple对象中的元素是被紧密的存储的(位于连续的内存区域),而不是链式结构。
#include<iostream>
#include<tuple>
#include<vector>
using namespace std;
int main()
{
//声明并创建一个元组对象:tuple类构造函数
tuple <int ,int , vector<int>>tupleText(100,200,{300,400,500});
//求元组对象的成员数量(参数个数):tuple_size函数
int tupleTextSize = tuple_size<decltype(tupleText)>::value;
cout<<"tupleText's size is:"<<tupleTextSize<<endl;//3
//求元组对象首个成员变量的值:get函数
auto first = get<0>(tupleText);
cout<<"The first value is:"<<first<<endl;//100
auto third = get<2>(tupleText);
for(const auto &iter:third)
{
cout<<"The third value:"<<iter<<" ";
}
//生成一个新的tuple对象:make_tuple函数
auto catTupleText = make_tuple(1,4);
//元组合并:tuple_cat函数
auto newTuple = tuple_cat(catTupleText,tupleText);
int newTupleSize = tuple_size<decltype(newTuple)>::value;
cout<<"\nThe newTuple's size is:"<<newTupleSize<<endl;//5
return 0;
}
lambda表达式用于创建并定义匿名的函数对象
1.lambda表达式基本语法
- 它以[]为开始,这个标识叫做捕获指定器,它告诉编译器我们要创建一个lamdba表达式。一个lambda表达式具有如下形式:
[capture list] (parameter list) -> return type {function body}
#include<iostream>
using namespace std;
int main()
{
auto func1 = [](){cout<<"HelloWorld"<<endl;};
//这个auto关键字可以推断func1变量的类型为函数指针类型
cout<<func1<<endl;//1
func1();
auto func2 = [](int a){cout<<a<<endl;};
func2(100);
return 0;
}
- lambda表达式与STL
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
vector<int>ve {100,200,300};
//遍历方式1
for_each(ve.begin(), ve.end(),[](int val)
{
cout<<val<<" ";
});
//遍历方式2
for(auto x:ve)
{
cout<<x<<" ";
}
//遍历方法3
for(auto x = ve.begin();x != ve.end();x++)
{
cout<<*x<<" ";
}
return 0;
}
- 指定lambda返回类型:如果你的lambda函数没有return语句,则默认返回void。假如你有一个简单的返回语句,编译器将推导返回值的类型,因此无需指定返回类型,但也有例外,所以最好指定返回类型。
2.lambda表达式的完整变量捕获:
- [] 不捕获任何变量
- [&] 以引用方式捕获所有变量。note:当以引用方式捕获一个局部变量时,必须确保在lambda执行时变量是存在的。
- [=] 用值的方式捕获所有变量(可能被编译器优化为const &)。23属于隐式捕获。
- [=, &foo] 以引用捕获foo, 但其余变量都靠值捕获
- [bar] 以值方式捕获bar; 不捕获其它变量
- [this] 捕获所在类的this指针
#include<iostream>
using namespace std;
class Example
{
private:
int x;
public:
Example():x(100){}
void Show()const
{
[this]()//捕获所在类的指针
{
cout<<x<<endl;
}();//调用lambda表达式
}
};
int main()
{
Example ex;
ex.Show();
return 0;
}
3.可变lambda
默认情况下,对于一个值被拷贝的变量,lambda不能改变其值,因为是只读的。如果希望改变一个采用值捕获方式的变量的值,就必须在参数列表首加上关键字mutable,这样就可以在lambda函数体改变捕获的变量的值,但是外部的变量没有影响,因为是值捕获。
int val = 100;
auto func = [val]()mutable
{
// 如果没有mutable关键字
// error: increment of read-only variable 'val'
return ++val;
};
cout << func() << endl; // 101
cout << val << endl; // 100
新的std::function是传递lambda函数的最好的方式,不管是传递参数还是返回值。它允许你在模板中指定参数列表和返回值的确切类型。function定义在’functional’头文件中。
#include<iostream>
#include <functional>
#include <vector>
#include<string>
using namespace std;
class AddressBook
{
public:
vector<string> findMatchingAddresses (function<bool (const string&)> func)
{
vector<string> results;
for ( auto itr = _addresses.begin(), end = _addresses.end(); itr != end; ++itr )
{
// 调用传递到findMatchingAddresses的函数并检测是否匹配规则
if ( func( *itr ) )
{
results.push_back( *itr );
}
}
return results;
}
private:
vector<string> _addresses;
};
constexpr函数
常量表达式主要是允许一些计算发生在编译时,即发生在代码编译而不是运行的时候。这是很大的优化.为了使函数获取编译时计算的能力,你必须指定constexpr关键字到这个函数的返回值前。
explicit关键字
explicit关键字在Cpp11以前是只能用于修饰构造函数,但在C++11中可以用来修饰操作符,上面代码中的operator bool()加上explicit表示其无法隐式转化为bool。
#include<iostream>
using namespace std;
struct Testable
{
explicit operator bool() const {
return false;
}
};
int main()
{
Testable a, b;
if (a) { }
if (a == b) { } // 编译错误,这里无法将a,b隐式
//转换为bool.
}
利用c++11的新特性在类内初始化成员变量
1.对非静态成员数组变量进行初始化
#include<iostream>
using namespace std;
class Example
{
private:
int x[5];
public:
Example():x{1,2,3,4,5}{}
void Show()const
{
for(auto y:x)
{
cout<<y<<" ";
}
}
};
int main()
{
Example ex;
ex.Show();
return 0;
}
2.对非静态非数组成员变量进行初始化
#include <iostream>
using namespace std;
class Example
{
public:
Example(int x):x(x){}
int GetX()const
{
return x;
}
static int GetY()
{
return y;
}
Example()
{
}
private:
int x{ 100 };
//int x = 100;//可以使用等号或者大括号版本的初始化
const static int y = 200;
//static int y = 200; // error
};
int main()
{
Example ex;
cout << ex.GetX() << endl;
cout << Example::GetY() << endl;
//基于范围的for循环可以简化代码的编写量
int arr []= {100,200,300,400};
for (auto x : arr)
{
cout << x << " ";
}
return 0;
}
3.对类内常量的静态成员变量直接初始化(非常量的静态成员变量不能在类内直接初始化)
右值引用和move(移动)语义
1.右值引用
传统的cpp引用(左值引用)关联到左值。c++11增加了右值引用,这是使用&&表示的,右值引用可以关联到右值。通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。右值引用的赋值表达式右边一般是常量。
#include <iostream>
using namespace std;
int main()
{
//右值引用举例
int && a = 100; //若引用是常引用,则a的值不可更改
cout << &a << endl;//获取100这个元素的地址
cout << a << endl;
a = 100000;//此时a的地址值还是100的地址
cout << &a << endl;
cout << a << endl;
int x = 1000;
int y = 1000;
int && b = x + y;//右边是表达式
cout << b << endl;
return 0;
}
2.移动构造函数与移动赋值运算符函数
转移构造函数(移动构造函数)的工作:把资源从一个对象转移到另一个对象,而不是复制它们。一般参数是左值则调用拷贝构造函数,右值则调用移动构造函数。源对象本身必须不能被改变,作为左值。用户不再使用的对象作为右值。
#include<iostream>
#include<memory>
using namespace std;
class Example
{
private:
unique_ptr<int>_member;//智能指针类对象,为左值
public:
//Example(unique_ptr<int>&&member):_member(member){}
//error,因为对右值member的转移是不允许的
};
int main()
{
return 0;
}
c++11新增关键字noexcept:
其语法为:
- noexcept:等同于noexcept(true)
- noexcept(expression)
If the value of the constant expression is true, the function isdeclared to not throw any exceptions.
noexcept without a constant expression is equivalent to noexcept(true).
标准库bind函数
bind函数定义在functional头文件中,可以将bind函数看作一个通用的函数适配器,他接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。
// arg_list中的参数可能包含入_1,_2等,这些是新函数newCallable的参数。
auto newCallable = bind(callbale, arg_list);
_1,_2等,是放在了命名空间placeholder中,所以要使用:
//_1,_n定在std::placeholders里面
using namespace std::placeholders;
bind参数用法:
//g是以个有2个参数的可调用对象
auto g = bind(func, a, b, _2, c, _1);//func是有5个参数的函数
调用g(X, Y), 等于 func(a, b, Y, c, X)
绑定引用参数:默认情况下,bind的那些不是占位符的参数被拷贝到返回的可调用对象中。但是有时希望以引用方式传递绑定的参数,或者要绑定参数的类型不能拷贝。(比如ostream)
ostream& print(ostream& os, const string &s, const char &c)
{
return os << s << c;
}
//bind引用,必须使用ref或者cref函数,把对象转化成引用,不能用&
ostream &os = cout;
const char c = ' ';
vector<string> svec{"aab","d","aa","bb","e","bbb"};
for_each(svec.begin(),svec.end(),bind(print, ref(os), _1, cref(c)));
新增函数定义语法,在函数名和参数列表后指定函数返回类型
#include "pch.h"
#include <iostream>
#include <vector>
using namespace std;
auto Sum(int x)->int
{
return x;
}
int main()
{
cout << Sum(100) << endl;
//c++为标识符创建别名提供了typedef,c++11提供了创建
//别名的新语法using=
vector<int>vec;
for (int i = 1; i < 3; i++)
{
vec.push_back(i);
}
using itType = vector<int>::iterator;
itType it = vec.begin();
cout << *it << endl;
//c++11新增了一种作用域内的枚举类型,使用关键字
//struct或者class来定义
enum class color{red,white,yellow};
//如上,需要使用red时,需要加上color::red;
int a = int(color::red);
cout << a << endl; // 0
return 0;
}
delete和default关键字的作用
- c++11使用关键字default来指定声明一些编译器可以默认提供的构造函数、赋值函数、析构函数这些方法.这样就可以使用编译器生成的函数。
- 禁用函数的用法:关键字delete则指定禁止使用编译器提供的某个方法。(对任何成员函数都适用)
#include <iostream>
using namespace std;
class Example
{
private:
int x;
public:
Example() = delete;//将导致类无法使用默认构造函数
Example(int x):x(x){}
int GetX()const
{
return x;
}
//导致调用默认拷贝构造函数
Example(const Example & ex) = default;
};
int main()
{
//Example ex;//无合适的函数可以调用
Example ex(100);
cout << ex.GetX() << endl;
Example ex1 = ex;
cout << ex1.GetX() << endl;
return 0;
}
委托构造函数
如果给类提供了多个构造函数,可能需要重复编写相同的
代码。委托构造函数允许在一个构造函数中使用另一个构
造函数。