C++11新玩法

默认成员函数的控制

1 语法上:
如果用户没有显示定义构造,拷贝构造等默成员函数,编译器会自动生成一个默认的成员函数。

实际上: 并不是,有的编译器会,但编译器不会。
eg VS2013会看情况而定,如果真的需要再生成,不是真的需要就不生成。

2 拿构造函数来说:
1 组合 : 如果A类含有B类对象,那么会默认生成(因为B类对象初始化列表)

2  继承: 如果发生继承时,基类带有无參或全缺省的构造函数,那么编译器会默认生成派生类的构造函数

3  在虚拟继承时,在构造对象阶段将指向虚函数表的指针放在对象的前四个字节

4 如果类中含有虚函数,需要在构造对象阶段将指向虚函数表的指针放在对象的前四个字节。

智能指针中 :
C++和Boost的智能指针
unique_ptr 独一无二的ptr;
实现原理 :
**防拷贝,防赋值,**c++98私有化只声明,而c++11 delete 告诉编译器删除默认的成员函数

new
c++11 之前,不能用new来创建默认对象,
eg: int* p = new int 【10】{123456459};
c++11 就可以。

多态中

虚函数重写-------多态的重要条件之一
在多态中可能会在重写虚函数时,写反了一两个字母而不容易发现,C++提供override这个编辑器来严格检查,早发现问题,早修改问题

注意override只能修饰派生类中的虚函数,不能修饰基类中的虚函数。

final :
修饰类时,该类不能被继承
修饰函数::final修饰函数必须是虚函数,作用,禁止该虚函数在其子类中被重写

题外话:shared_ptr

shared_ptr   引用计数的方式来管理内存
1	方式 引用计数(指针,因为需要修改计数,引用计数是多个智能指针对象共享的)

2	引用计数的空间在堆上维护,因此new  delete来管理 
3  线程安全问题,解决方式,引用计数修改之前加上互斥锁。

shared_ptr循环引用问题类似于死锁
解决方式 :将shared_ptr换成weak_ptr若指针
weak_ptr 就是用来解决shared_ptr循环引用的问题

C++98 : auto_ptr :
C++11 shared_ptr &&weak_ptr

C++ 11 引入的序列化容器
forward_list :单链表

C++98不支持动态数组:
eg: int * a =new int[4 ]{2345}

但C++11可以

列表初始化(注意不是初始化列表)
class A{1,2 }

初始化列表是初始化一个对象,
列表初始化初始化多个对象

C++的类型推断有两种风格:
1 auto

2 decltype
主要用于推倒表达式的类型

运行时类型推断:
1 typeid( x ).name()
2 dynamic_cast() //强制类型转换的推演,成功则转换,失败,返回0
dynamic_cast() 你需检测含有虚函数的类中。

decltype:
可以作为检测表达式的返回值,
借助typeid().name() 可以显示返回值类型。
推演期间不会调用函数,因为推演发生在编译期间。

看下图你就明白了
在这里插入图片描述

引用 && 右值引用

c++98 引用

c++98既可以引用左值,也可以引用右值,需要引用左值可与➕const

引用变量和其引用的类型共用一段内存空间。

引用的底层实现原理,还是按照指针的方式实现。

引用就是对象的别名,他的出现弱化了指针。

在C++中需要用到一级指针可以用引用替代

二级指针就可以用一级指针的引用来替代,
处理二级指针复杂性比较高,那么一级指针的引用就可以简单的处理问题了。

例如二叉树的销毁,必须得销毁(每个)根节点,因此可以使用一级指针的引用。

C++11 右值引用

C++11 为了提高程序的运行效率,提出
右值引用。本质还是引用,只能引用右值

什么是右值?

eg T&& ref = 10;

什么是左值:
int a =10;
T&& b =a;

这样不行!!!
这样不行!!!
这样不行!!!

那什么是左值? 什么是右值?

C语言提出这个概念,但也比较模糊。

一般来说:
1 要么 只能放在 等号左边
2 要么 能够取地址的称为左值

1 const 类型变量(可以引用)
2 临时遍历
3 表达式的结果

函数如果按照引用的的方式返回,可以作为左值,因为返回的就是本身。

C++11 严格规定右值:

1 纯右值。 eg: a+b, 100 ,
2 将亡值,比如表达式的中间结果。又比如 函数返回的临时结果。

C++11 认为不符合右值的全是左值

我感觉C++11标准就看是不是存储在新开辟出来空间的,开辟空间了称为 右值。

C++11只能引用右值,不能直接引用左值,若真的需要c++提供了一move方法,将左值转化成了右值。

右值引用核心:

移动语义||资源转移

如果第二个对象是在复制或赋值结束后被销毁的临时对象,则调用移动构造函数和移动赋值运算符,这样的好处是避免深度复制,提高效率。

移动构造和移动赋值配合使用:
string a ,b, string c =() b + c;
b+c 形成一个 tmp 将进行一次拷贝构造 ,这个tmp是一个临时值,这是又使用移动构造,将这个将亡值给偷梁换柱掉。

移动构造 

思想:
将一个要销毁的资源移动到需要它的资源里面去,就不用创建临时对象接收将销毁对象的资源,也不用销毁临时对象。

而C++11 正好认为将亡值是一个右值,
那么我们就可以进行右值引用。

举个栗子🌰:

注意看 oprerate + 和 移动构造函数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>

namespace N
{
	class String
	{
	public:
		String(const char* str = "" )
		{
			if (str == nullptr)
				str = "";
			str_ = new char[strlen(str) + 1];
			strcpy(str_, str);
		}

		String(const String& s)
			:str_ (new char[strlen(s.str_) + 1])
		{		
			strcpy(str_, s.str_);
		}

		//右值引用 : 1 将亡值  2  右值  
		//移动构造 :把将亡值的资源移动出来
		String(String&& a)
			:str_(a.str_)
		{
			a.str_ = nullptr;//当然也可以swap
		}
		//移动语义||资源转移

		//仔细分析这个函数做了什么
		//s3 = s2 + s1;    内部创建了一个ret; 传值值返回,右边需要一个temp接收s1 + s2值, 
		//即  s2 + s1 --->temp生成;   s3 = temp;  temp 销毁  所以总共创建了3次,销毁了两次
		String operator + (const String& a)
		{
			int len1 = strlen(str_);
			char* tmp = new char[len1 + strlen(a.str_) + 1];
			strcpy(tmp, str_);
			strcpy(tmp + len1, a.str_);
			
			String ret(tmp);//创建一份局部对象
			delete[] tmp;//防止内存泄漏
			return ret;//返回局部对象后,便被销毁 ,在返回值那里 又创建一个Temp临时对象接收返回值,
		}

		String& operator = (const String& s)
		{
			//先申请空间,避免申请空间失败了,又把原来的内容删除了
			char* tmp = new char[strlen(s.str_) + 1];
			strcpy(tmp, s.str_);
			delete[] str_;
			str_ = tmp;
			return *this;
		}
		~String()
		{
			if (str_)
				delete[] str_;
			str_ = nullptr;
		}




	private:
		char* str_;
	};
}
	int main()
	{
		N::String a("HELLO");
		N::String b(" world");
		N::String c = a + b;

		//std::cout << c << std::endl;
		return 0;
	};

在这里插入图片描述
在这里插入图片描述

看到没资源转移了?避免了刚释放空间又申请空间,大大提高了程序的运行效率!!

完美转发:

所谓完美转发其实就是一个函数模版

实现原理 :
模版函数在接收来不同的类型函数时,按照各自类型的转发给另一个函数去执行, 不产生额外开销!!
不产生额外开销!!
不产生额外开销!!

类似于代理 分类+转发,
就像模版转发函数并不存在似的。完美吧?

注意: 完美转发的模版函数并不更改参数的左右值属性, 目的是因为,让目标函数k可能对原有数据的左右值属性进行不同的处理。

C++11 用 forward函数来实现完美转发。
头文件 #include

C++ lambda表达式

什么是lambda表达式?

在函数中声明函数称为lambda表达式:

eg: int Add(int a, int b , &a mutable-> return-type{ return a++;} )
{ return a + b; }

你可能会问函数名是啥呀?
注意: lambda表达式可以看作是一个无名函数。

为什么提出 lambda表达式?

在一个函数中,可能有部分代码重复出现,导致冗余,解决办法:

1 将重复代码封装程一个函数(有时候,只有一两个函数用,别的函数用不到,所以也别是怎么可观)

2 写个仿函数,但每次调用都要构造一个类,很麻烦,
class Add
{
public:
int operator () (int& a, int &b)
{
return a + b;
}
};

仿函数内部重载一下需要的运算符。

eg: int A(10, 20, Add()){ };

2 方法三: 在函数中声明函数->lambda表达式

lambda表达式 :以【】开始

父作用域: lambda表达式所在的代码块

【】 :不捕获父作用域的成员变量

【=】:按照值的方式捕获父作用域的所用变量,不会影响父作用域

【&】:按照引用的方式捕获父作用域中的所有变量,会影响父作用域

【var】 安值的方式捕获父作用域中的var变量

【this】 捕获父成员中的this 指针

【&var】安引用的方式捕获父作用域中的var变量

参数列表: ()需要则写,不需要时不写

mutable :
lambda表达式返回值默认情况下是const 类型, 加上mutable 可以取消返回值的常量属性,但是前提是必须加上()参数列表。

->return -type :
用追踪返回值类型 来声明函数返回值类型,没有返回值时,可以省略,也可以直接省略,由编译器推导。

因为返回值类型和参数列表可以不写,而【】可以为空,因此C++11中最简单的lambda函数: 【】{ } 啥都不做。

lambda函数可以理解为无名函数,,因为没有名字,所以不能直接调用,因此如果想直接调用,那就需要用auto 一个变量来推导接收返回值类型。
因此这个变量为函数指针类型。

lambda捕获列表不能重复捕获,
lambda放在块作用域以外,捕获列表为空,即什么都捕获不到
lambda表达式只能捕获父作用域中的局部变量
不同类型的lambda表达式之间不能相互赋值

C++11 线程库

库,肯定跨平台

C++11对线程进行了支持,因此c++并行编程不需要依赖第三方库,
而且还**引入了原子类型,**很多情况可以避免了加锁带来的消耗。

#include
该头文件声明了std::thread线程类

1 线程的启动

C++线程库构造一个对象来启动一个线程,该对象包含了线程运行时上下文信息,eg:线程函数,线程栈,线程起始状态线程id等所有操作全部封装在一起,最后在底层统一传递给_beginthreadex()创建线程函数来实现。

2 线程的结束

join :加入式:主动等待线程终止,,join会清理线程资源,然后返回,由于join已经清理了资源,thread对象与已销毁的线程就没有任何关系了,因此一个线程对象每次只能使用一次join()

detach:分离式
detach会从线程中分离出新的线程,之后不能在与新线程交互,detach线程会在后台运行,其所有权和控制权交给C++运行库。
同时c++运行库保证,当线程退出时,线程资源将被回收。

3 原子性操作库atomic

线程最大的问题就是线程安全问题,C++98 手段是加锁,但加锁势必影响程序运行效率
c++11提供了原子操作库。
#include

实质是对数据类型的原子化。

4 mutex 互斥量(锁)

1 基本的std::mutex
lock()
unlock()
trylock()

2 recursive_mutex 递归锁

允许递归的形式多次上锁。

3 std::timed_mutex
try_lock_for
try_lock_until

lock_guard

采用RAII方式,对锁进行封装,是一个类模版。
适合简单的加锁解锁

unique_lock

采用RAII方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值