简介本文主要介绍C++11新特性: 1、auto;2、nullptr空指针;3、for容器遍历;4、lambda表达式;5、override、final;6、右值引用;7、move构造函数;8、容器初始化。
类型推导
从初始化表达式中推断出变里类型
auto a; // 错误,auto是通过初始化表达式进行类型推导,如果没有初始化表达式,就无法确定a的类型
auto i = 1;
auto d = 1.0;
auto str = "Hello World";
auto ch = 'A';
auto func = less<int>();
vector<int> iv;
auto ite = iv.begin();
auto p = new foo() // 对自定义类型进行类型推导
decltype类型推导
auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型。 但有时候可能需要根据表达式运行完成之后,对结果的类型进行推导,因为编译期间,代码不会运行,此时auto也就无能为力。
看下面例子:
// 任意两个数的加法
template<class T1, class T2>
T1 Add(const T1& a, const T2& b)
{
return a + b;
}
需要改为:
// 任意两个数的加法
template<class T1, class T2>
auto Add(const T1& a, const T2& b)
-> decltype(a+b)
{
return a + b;
}
decltype实际上有点像 auto的反函数 ,auto可以让你声明一个变量,而 decltype则可以从一个变量或表达式中得到类型 ,示例如下:
int x = 3;
decltype(x) y = x;
nullptr是为了解决原来C++中NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0,
以往我们使用NULL表示空指针。它实际上是个为0的int值。
void F(int a){
cout<<a<<endl;
}
void F(int *p){
assert(p != NULL);
cout<< p <<endl;
}
int main(){
int *p = nullptr;
int *q = NULL;
bool equal = ( p == q ); // equal的值为true,说明p和q都是空指针
int a = nullptr; // 编译失败,nullptr不能转型为int
F(0); // 在C++98中编译失败,有二义性;在C++11中调用F(int)
F(nullptr);
return 0;
}
在C++11 中for循环可以使用类似java的简化的for循环 ,可以 用于遍历数组,容器,string以及由begin和end函数定义的序列 (即有Iterator),示例代码如下:
void show()
{
// 遍历数组
int array[] = { 1,2,3 };
for (auto e : array)
{
cout << e << endl;
}
// 容器遍历
map<string, int> m{ { "a", 1 },{ "b", 2 },{ "c", 3 } };
for (auto p : m) {
cout << p.first << " : " << p.second << endl;
}
// string遍历
string str = "C++实战网(www.cppszw.com)";
for (auto s : str)
{
cout << s << endl;
}
}
ambda表达式类似Javascript中的闭包,它可以用于创建并定义匿名的函数对象,以简化编程工作。
Lambda的语法如下:
[函数对象参数](操作符重载函数参数)->返回值类型{函数体}
vector<int> iv{5, 4, 3, 2, 1};
int a = 2, b = 1;
for_each(iv.begin(), iv.end(), [b](int &x){cout<<(x + b)<<endl;}); // (1)
for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);}); // (2)
for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);});// (3)
示例:
// 使用lambda表达式计算字符串个数以及大写字母个数
int lambda()
{
int count = 0;
// auto用于获取lambda表达式返回值
auto func = [&count](string str)->int // lambda:表达式
{
int upper = 0;
for (auto c : str)
{
count++;
if (isupper(c))
upper++;
}
return upper;
};
int upper = func("C++实战网(www.cppszw.com)");
cout << "alpha counts:" << count << endl;
}
class base
{
public:
virtual void function(short)
{
cout << "base function" << endl;
}
// 使用fina1修饰,表明禁止派生类重写虚函数
virtual void show() const final
{
cout << "base show" << endl;
}
};
class derived :public base
{
public:
// 使用override修饰表示重写基类的虚函数
virtual void function(short) override
{
cout << "derived function override" << endl;
}
virtual void show()
{
cout << "derived show" << endl;
}
};
extern int x;
// 返回值可以做左值,左值定义:拥有地址的表达式,左值指向的是一个稳定的内存空间
int&getvalue()
{
return x;
}
// 右值指向的不是一个稳定的内存空间,而是一个临时空间
double getage()
{
double age = 12.4;
return age;
}
// 左值和右值的联系:(1)右值给左值赋值(2)可用const左值引用绑定一个右值
double leftv = getage();
// cost左值引用并不是一个左值,没有建立一个稳定的内存空间,若不是const的话,可以修改其值,右值不能赋值
// 因此必须使用const左值引用,如果使用double&xef = getage()会出错
const double&refv = getage();
// 右值引用:可以使用右值引用显示绑定一个右值,语法是“&&”,指定了右值引用,const.或者非const都正确
double&& refright = 12.3;
const double&& crightref = 12.3;
// 重载左值和右值
void show(double& left)
{
cout << "left value " << endl;
}
void show(double&& right)
{
cout << "right value " << endl;
}
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
class Str{
public:
char *str;
Str(char value[])
{
cout<<"origin construct function..."<<endl;
str = NULL;
int len = strlen(value);
str = new char[len + 1];
memset(str,0,len + 1);
strcpy(str,value);
}
Str(const Str &s)
{
cout<<"copy construct function..."<<endl;
str = NULL;
int len = strlen(s.str);
str = new char[len + 1];
memset(str,0,len + 1);
strcpy(str,s.str);
}
Str(Str &&s)
{
cout<<"move construct function..."<<endl;
str = s.str;
s.str = NULL;
}
~Str()
{
cout<<"destruct funciton..."<<endl;
if(str != NULL)
{
delete []str;
str = NULL;
}
}
};
int main()
{
char value[] = "Hello Word!";
cout<<"step 1"<<endl;
Str s(value);
cout<<"step 2"<<endl;
Str ss(move(s));
cout<<"step 3"<<endl;
Str ss1 = ss;
cout<<"step 4"<<endl;
Str ss2 = static_cast<Str&&>(ss1);
cout<<"finish"<<endl;
return 0;
}
编译输出:
step 1
origin construct function...
step 2
move construct function...
step 3
copy construct function...
step 4
move construct function...
finish
destruct funciton...
destruct funciton...
destruct funciton...
destruct funciton...
在引入C++11之前,只有数组能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:
int arr[3] = {1, 2, 3}
vector<int> v(arr, arr + 3);
在C++11中,我们可以使用 更加优雅的 语法来进行替换:
int arr[3]{1, 2, 3};
vector<int> iv{1, 2, 3};
map<int, string>{{1, "a"}, {2, "b"}};
string str{"Hello World"};
内容来源于网络,由C++实战网(www.cppszw.com)收集整理。
本文向大家介绍一个C++实战项目:C++实现雪花算法(SnowFlake)产生唯一ID,主要涉及雪花算法、算法知识等,具有一定的C++实战价值,感兴趣的朋友可以参考一下。
本文介绍一个C++代码片段:如何在C++中删除一个文件目录下的所有文件及目录,感兴趣的朋友可以参考一下。
本文介绍C++实现C++实现8种排序算法,主要包括冒泡排序、插入排序、二分插入排序、希尔排序、直接选择排序、堆排序、归并排序、快速排序,直接上代码,感兴趣的朋友可以参考一下。
本文介绍C++实现线程同步的四种方式:事件对象、互斥对象、临界区、信号量,感兴趣的朋友可以参考一下。
本文介绍C++内存泄漏的检测与定位方法,感兴趣的朋友可以参考一下。
本文向大家介绍一个C++实战项目:C++实现一个多线程安全的队列容器模板类,主要涉及C++模板类的使用、互斥体实现多线程安全、队列数据结构等知识,具有一定的C++实战价值,感兴趣的朋友可以参考一下。