C++之C++11

一、默认成员函数的控制

1.显式缺省函数

  • 在C++11中,可以在默认函数定义或者声明时加上=default,从而显式的知指示编译器生成该函数的默认版本,用=default修饰的函数称为显式缺省函数。
class A
{
public:
	A(int a)//有了该构造函数,编译器不会自动生成默认的构造函数
		:_a(a)
	{}
	//显示缺省构造函数,由编译器生成
	A() = default;//加了default此句代码后,即使我们写了构造函数,编译器也会自动生成默认的构造函数

	//在类中声明,在类外定义时让编译器生成默认赋值运算符重载
	A& operator=(const A& a);//加了default此句代码后,编译器会自动生成默认的赋值运算符重载
private:
	int _a;
};
A& A::operator=(const A& a) = default;//在类外定义

int main()
{
	A a(30);
	A b;

	b = a;

	return 0;
}

在这里插入图片描述

2.删除默认函数

  • 在C++98中,如果想要限制某些默认函数的生成,就将该函数权限设置为private,且不定义,这样只要类外想调用就会报错。在C++11中,如果想要限制某些默认函数的生成,只需在该函数声明加上=delete即可,该语法使编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
class A
{
public:
	A(int a)
		:_a(a)
	{}


	//禁止编译器生成默认的拷贝构造函数
	A(const A& a) = delete;

	//禁止编译器生成默认的赋值运算符重载
	A& operator=(const A& a) = delete;
	
private:
	int _a;
};


int main()
{
	A a(30);
	//编译器报错,因为该类的拷贝构造函数已经删除
	A b(a);

	//编译器报错,因为该类的赋值运算符重载函数已删除
	b = a;

	return 0;
}

在这里插入图片描述

二、final和override关键字

1.final关键字

  • fina加到某个类或某个虚函数之后,l可以修饰某个类或某个虚函数,被修饰的类就不可以被继承,被修饰的虚函数就不可以被子类重写。
#include <iostream>

class A final
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a = 0;
};
class B
{
public:
	virtual void Print() final
	{
		std::cout << "B类" << std::endl;
	}
};

class C : public A
{};

class D : public B
{
public:
	void Print()
	{
		std::cout << "D类" << std::endl;
	}
};

在这里插入图片描述

2.override关键字

  • override加在某个派生类的虚函数之后,如果派生类对该虚函数没有进行重写,则报错。用来检查派生类是否重写了基类的某个虚函数。
#include <iostream>

class A
{
public:
	A(int a)
		:_a(a)
	{}
	virtual void Print() 
	{
		std::cout << "A类" << std::endl;
	}
private:
	int _a = 0;
};
class B : public A
{
public:
	//正确
	void Print() override
	{
		std::cout << "B类" << std::endl;
	}
	//不报错
    void print()
	{
		std::cout << "B类" << std::endl;
	}
	
	//报错
	void print()override
	{
		std::cout << "B类" << std::endl;
	}
};

在这里插入图片描述

  • 如上述例子可以得出,如过子类要对父类的虚函数进行重写,但是不小心在子类中写错函数名字,就达不到想要的目的,此时在要重写的虚函数之后加上override关键字,如果写错函数名字就会报错。

三、变量的类型推导

  • C++98中已经支持RTTI(运行时类型识别,Run-Time Type Identification):

① typeid只能看类型,不能用其结果来定义类型。typeid(v),name()查看对象v类型。

#include <iostream>

using namespace std;

int main()
{
	int c = 20;

	cout << typeid(c).name() << endl;

	return 0;
}

在这里插入图片描述
② dynamic_cast只能用于含有虚函数的继承体系中。dynamic_cast< type-id > (expression) 该运算符把expression转换成type-id类型的对象。将一个基类对象指针(或引用)cast到派生类指针。dynamic_cast会根据基类指针是否真正指向派生类指针来做相应处理,即会作出一定的判断。:若对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;若对引用进行dynamic_cast,失败抛异常,成功返回正常cast后的对象引用。

#include <iostream>

using namespace std;

class A
{
public:
	virtual void fun()
	{
		cout << "A::fun()" << endl;
	}
};
class B : public A
{
public:
	void fun()
	{
		cout << "B::fun()" << endl;
	}
};

int main()
{
	B b;
	A* pa;

	pa = dynamic_cast<A*>(&b);
	pa->fun();
	
	return 0;
}

在这里插入图片描述

1.auto

  • auto是C++11中的类型推导,通过初始化来推导变量类型。auto e = _____通过初始化去推导,给你初始化什么,e类型就是什么。
#include <iostream>

using namespace std;


int main()
{
	int a = 0;
	auto b = a;
	cout << "b的类型为:" << typeid(b).name() << endl;

	char c = 'a';
	auto d = c;
	cout << "d的类型为:" << typeid(d).name() << endl;

	double e = 8.99;
	auto f = e;
	cout << "f的类型为:" << typeid(f).name() << endl;

	int* p = &a;
	auto q = p;
	cout << "q的类型为:" << typeid(q).name() << endl;

	return 0;
}

在这里插入图片描述

2.decltype

  • C++11中用于推导表达式的结果的类型。
#include <iostream>

using namespace std;


int main()
{
	int a = 0, b = 3;
	decltype (a + b)c;
	cout << "c的类型为:" << typeid(c).name() << endl;

	double e = 8.99, g = 2.89;
	decltype (e - g)h;
	cout << "h的类型为:" << typeid(h).name() << endl;


	return 0;
}

在这里插入图片描述

四、范围for

  • 范围for对于容器而言,底层是迭代器支持的。
#include <iostream>
#include <vector>

using namespace std;


int main()
{
	vector<int> vc = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

	for (auto e : vc)
		cout << e << " ";
	cout << endl;

	return 0;
}

在这里插入图片描述

  • 当e是拷贝代价大(深拷贝)的对象时,注意用&,如string、vector< string >等。
#include <iostream>
#include <string>

using namespace std;


int main()
{
	string str = "abcdefghijklmn";
	for (auto& e : str)
		cout << e << " ";
	cout << endl;

	return 0;
}

在这里插入图片描述

五、初始化列表

1.基本使用

#include <iostream>
#include <vector>
#include <set>
#include <map>

using namespace std;


int main()
{
	vector<int> vc1 = { 1, 2, 3, 4, 5, 6, 7 };
	vector<int> vc2{ 1,2,3,4,5,6,7 };//'='可以加可以不加

	int* p1 = new int(10);//C++98
	//int* p2 = new int[5](1);//C++98不支持new数组的初始化
	int* p3 = new int[7]{ 1,2,3,4,5,6,7 };

	set<int> s1 = { 7,5,4,6,1,2,3 };
	set<int> s2{ 7,5,4,6,1,2,3 };//'='可以加可以不加

	map<string, int> mp1 = { {"苹果", 5}, {"香蕉", 7}, {"橘子", 3}, {"西瓜", 3} };
	map<string, int> mp2{ { "苹果", 5 },{ "香蕉", 7 },{ "橘子", 3 },{ "西瓜", 3 } };//'='可以加可以不加

	return 0;
}

2.自定义类型

  • C++98不支持多对象的列表初始化
#include <iostream>

using namespace std;

//C++98
class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a = 0;
};
int main()
{
	//调用构造函数
	A a1(9);
	
	//类型转换,先构造一个_a的值为2的临时变量对象
	//再用该临时变量对象拷贝构造a2
	//编译器会优化成该语句就是直接构造
	A a2 = 2;

	return 0;
}
  • C++11支持多对象的列表初始化
#include <iostream>

using namespace std;

//C++98
class Point
{
public:
	Point(int x = 0, int y = 0)
		:_x(x)
		,_y(y)
	{}
	void Print()
	{
		cout << _x << ", " << _y << endl;
	}
private:
	int _x = 0;
	int _y = 0;
};
int main()
{
	Point p1(1, 1);
	Point p2 = (2, 6);

	p1.Print();
	p2.Print();

	return 0;
}

在这里插入图片描述

  • 上述例子中,_x = 2,_y = 0,而不是_x = 2,_y = 6。因为编译器将(2, 6)当作了逗号表达式。返回值是6,所以传递的是p2 = 6。

3.支持初始化列表的原理

#include <iostream>

using namespace std;

int main()
{
	initializer_list<int> l = { 1,2,3,4,5 };
	initializer_list<int>::iterator it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

  • vector< int > vc = {1, 2, 3, 4, 5};
  • C++11中,用vector initializer_list< value_type > il, const allocator_type& alloc = allocator_type(1);进行初始化,其他容器也一样。

六、右值引用

1.左值引用和右值引用的概述

  • 左值引用和右值引用都是引用,即都是给对象取别名。
  • 可以放在‘’=‘’左边的,或者能够取地址的称为左值;只能放在‘’=‘’右边的称为右值的,或不能取地址的称为右值。
#include <iostream>

using namespace std;

int main()
{
	int a = 10;//a是一个左值,10是一个右值

	//在左边一定是左值,而在右边不一定是右值
	int b = a;//这里的a和b都是左值
	const int c = 10;//这里的c不能算是左值

	//左值通常是可以被修改的对象;右值通常是常量、表达式的返回值(临时对象)
	int d = a + b;//a+b返回的一个临时对象就是右值

	return 0;
}

2.左值引用&

#include <iostream>

using namespace std;

int main()
{
	//1.左值引用可以引用左值
	int a = 10;
	int& b = a;

	//2.左值引用不能引用右值,但const左值引用可以引用右值
	const int& c = 10;
	const int& d = a + c;

	return 0;
}

3.左值引用的应用

① 做参数,如 swap(T& a, T& b)为输出型参数。func(const vector< int >& v)做函数参数,提高了传递效率。
② 做返回值,如T& operator[] (size_t index)改变返回对象。 vector< T >& operator=(const vector< T >& v)提高了效率。

4.右值引用&&

#include <iostream>

using namespace std;

int main()
{
	//1.右值引用可以引用右值
	int a = 10, b = 10;
	int&& c = a + b;
	int&& d = 10;
	int&& e = a + 10;

	//2.右值引用不能引用左值,但可以用move将左值转换成右值
	int&& f = move(a);

	return 0;
}

5.右值引用的应用

  • 右值分为将亡值(函数或者表达式返回的临时自定义类型对象)和纯右值(如10、100、20、a+b等,常量和基本类型的临时变量)。

① 移动构造
在这里插入图片描述

  • 有了移动构造,这里因为s1+s2的返回值是右值(该临时对象就是将亡值即右值),那么就会自动匹配调用移动构造。
  • 本质:移动构造比拷贝构造效率更高,因为不涉及拷贝。

② 移动赋值
在这里插入图片描述

③ 移动构造、移动赋值和拷贝构造、拷贝赋值

  • 移动构造/移动赋值和拷贝构造/拷贝赋值对于普通类都一样,但是对于管理资源需要深浅拷贝的类、意义重大。如string、vector、list、map、set、unordered_map、unordered_set。
  • 在C++98中只能使用深拷贝(拷贝构造、拷贝赋值),而早C++11中可以使用移动构造、移动赋值。
    在这里插入图片描述
    ④ 右值引用的应用:emplace_back()和emplace。

6.完美转发(模板)

① 模板既可以匹配左值也可以匹配右值

#include <iostream>

using namespace std;

void fun(int& x)
{
	cout << "左值引用" << endl;
}
void fun(int&& x)
{
	cout << "右值引用" << endl;
}
void fun(const int& x)
{
	cout << "const左值引用" << endl;
}
void fun(const int&& x)
{
	cout << "const右值引用" << endl;
}

template<class T>
void perfectForward(T&& t)
{
	fun(t);
}
int main()
{
	perfectForward(10);//右值引用

	int a = 10;
	perfectForward(a);//左值引用
	perfectForward(move(a));//右值引用

	const int b = 10;
	perfectForward(b);//const左值引用
	perfectForward(move(b));//const右值引用



	return 0;
}

在这里插入图片描述

② 使用完美转发之后

#include <iostream>

using namespace std;

void fun(int& x)
{
	cout << "左值引用" << endl;
}
void fun(int&& x)
{
	cout << "右值引用" << endl;
}
void fun(const int& x)
{
	cout << "const左值引用" << endl;
}
void fun(const int&& x)
{
	cout << "const右值引用" << endl;
}

template<class T>
void perfectForward(T&& t)
{
	fun(std::forward<T>(t));
}
int main()
{
	perfectForward(10);//右值引用

	int a = 10;
	perfectForward(a);//左值引用
	perfectForward(move(a));//右值引用

	const int b = 10;
	perfectForward(b);//const左值引用
	perfectForward(move(b));//const右值引用



	return 0;
}

在这里插入图片描述

七、lambda表达式

1.函数指针、仿函数和lambda表达式

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

struct fruits
{
	string _name;
	double _price;
	int _weight;
};

//1.函数指针
bool PriceCompare(const fruits& f1, const fruits& f2)
{
	return f1._price < f2._price;
}
bool WeightCompare(const fruits& f1, const fruits& f2)
{
	return f1._weight < f2._weight;
}

//2.仿函数
struct PriceCompareObj
{
	bool operator()(const fruits& f1, const fruits& f2)
	{
		return f1._price < f2._price;
	}
};
struct WeightCompareObj
{
	bool operator()(const fruits& f1, const fruits& f2)
	{
		return f1._weight < f2._weight;
	}
};


int main()
{
	fruits f[] = { {"苹果", 2.22, 6}, {"西瓜", 3.43, 10}, {"橘子", 1.5, 4} };

	//1.函数指针
	PriceCompare(f[0], f[1]);
	sort(f, f + sizeof(f) / sizeof(f[0]), PriceCompare);
	sort(f, f + sizeof(f) / sizeof(f[0]), WeightCompare);

	//2.仿函数/函数对象
	/*
	比起函数指针的优势,即可作为类型在模板参数传递,也可以在函数参数作为对象
	*/
	PriceCompareObj pcoj;
	pcoj(f[0], f[1]);
	sort(f, f + sizeof(f) / sizeof(f[0]), pcoj);

	WeightCompareObj wcoj;
	wcoj(f[0], f[1]);
	sort(f, f + sizeof(f) / sizeof(f[0]), wcoj);


	//3.lambda表达式
	/*
	相比之前两种方式更加简单方便
	*/
	auto priceless = [](const fruits& f1, const fruits& f2)->bool 
	{
		return f1._price < f2._price; 
	};
	sort(f, f + sizeof(f) / sizeof(f[0]), priceless);
	//->bool也可以省略
	auto weightless = [](const fruits& f1, const fruits& f2)
	{
		return f1._weight < f2._weight;
	};
	sort(f, f + sizeof(f) / sizeof(f[0]), weightless);

	sort(f, f + sizeof(f) / sizeof(f[0]), [](const fruits& f1, const fruits& f2)
	{
		return f1._price < f2._price;
	});
	sort(f, f + sizeof(f) / sizeof(f[0]), [](const fruits& f1, const fruits& f2)->bool
	{
		return f1._weight < f2._weight;
	});

	return 0;
}

2.lambda表达式书写

  • [capture-list] ([arameters) mutable -> return-type {statement};
  • ① [capture-list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
    ②(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连()一起省略。
    ③mutable:默认情况下,lambda函数是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可以省略(即使参数为空,也不可以省略参数列表)。
    ④->return-type:返回值类型。返回值类型明确的情况下,也可以省略,由编译器对返回值类型进行推导。
    ⑤{statement}:函数体。

3.lambda表达式的举例(交换a和b的值)

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值