1、C++11有哪些新特性?

  • nullptr替换了NULL
  • 引入了auto和decltype这两个关键字实现类型推导
  • 基于范围的for循环for(auto& i : res){}
  • 类和结构体中初始化列表。
  • Lambda表达式(匿名函数)
  • std::forward_list(单向链表)
  • 右值引用和move语义

2、auto、decltype和decltype(auto)的用法

(1)auto

C++11新标准引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。和原来那些只对应某种特定的类型说明符不同。

auto让编译器通过初始值来进行类型推演。从而获得定义变量的类型,所以说auto定义的变量必须有初始值。

int a = 1, b = 2;
auto c = a + b; // a和b必须被初始化,所以在类型推演的时候需要使用a和b的类型。

(2)decltype

c++11新标准引入的第二种说明符decltype,它的作用是选择并返回操作数的数据类型。再次过程中,编译器只是分析表达式并得到它的类型,却不进行实际的计算表达式值。

intfunc(){return 0;}

decltype(func()) sum = 5;// 通过func()推断出sum的类型,并且最后存储一个5进去。

(3)decltype(auto)

decltype(auto)是c++14新增的类型指示符,可以用来声明遍历以及只是函数返回类型。使用时候,会将等号替换auto,然后使用decltype的方式推演出类型。

int e = 4;
const int *f = &e;
decltype(auto) j = f;
分析:auto会被f替换掉,变为 decltype(f) j = f;
有f是const int *类型的,所以j推导出来也是const int *类型。

3、C++中NULL和nullptr之间的区别?

NULL是C语言的宏定义;nullptr是c++11新增的关键字。

#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif

在C++中指针必须有明确的类型定义。但是将NULL定义为0带来的另一个问题是无法与整数的0区分。因为C++中允许有函数重载,所以可以试想如下函数定义情况:

#include <iostream>
using namespace std;

void fun(char* p) { // NULL == (void*)0
	cout << "char*" << endl;
}

void fun(int p) { // 如果NULL == 0
	cout << "int" << endl;
}

int main()
{
	fun(NULL);
	return 0;
}
//输出结果:int

4、智能指针的原理、常用的智能指针及实现

原理:智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配对象,防止堆内存泄漏。动态分配的资源,交给一个类对象管理,当类对象声明周期结束时,自动调用析构函数释放资源

c++11有shared_ptr、unique_ptr和weak_ptr三种类型的智能指针。(auto_ptr好像后面删除了)

shared_ptr(有一个环形引用的问题)

实现原理,采用计数器的方式,允许多个智能指针指向同一个对象,每当多一个指针指向该对象的时候,所有指向该对象的智能指针内部引用计数器加1,每当减少一个智能指针指向对象时,引用计数会见减1,当计数减少到0的时候,释放动态分配的资源。

unique_ptr

采用独享,每次只能由一个智能指针只想他。如果要转移一个unique_ptr指针那么会把所有权从源指针转移到目标指针,源指针置为空。

weak_ptr(用于解决shared_ptr环形引用的问题)

弱引用。引用计数有一个问题就是互相引用形成环(环形引用),这样两个指针指向的内存都无法释放。需要使用weak_ptr打破环形引用。weak_ptr是一个弱引用,它是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前使用函数lock()检查weak_ptr是否为空指针。

5、lambda函数的全部知识

格式
[capture] (parameters) mutable ->return-type {statement};

capture:用于捕获外面变量信息,在lambda函数内部可以直接使用。

parameters:lambda函数的参数信息

->return-type: 返回值类型

statement:具体的功能代码

当你使用lambda表达式的时候,编译器会自动生成一个匿名类(类只是重载了()运算符)。

6、智能指针的循环引用

循环引用是指使用多个智能指针shared_ptr时,出现了指针之间互相指向,从而形成环的情况,有点类似于死锁的情况,这种情况下,智能指针往往不能正常调用对象的析构函数,从而造成内存泄漏。举例说明:

#include <iostream>
using namespace std;

template <typename T>
class Node
{
public:
	Node(const T& value)
		:_pPre(NULL)
		, _pNext(NULL)
		, _value(value)
	{
		cout << "Node()" << endl;
	}
	~Node()
	{
		cout << "~Node()" << endl;
		cout << "this:" << this << endl;
	}

	shared_ptr<Node<T>> _pPre;
	shared_ptr<Node<T>> _pNext;
	T _value;
};

void Funtest()
{
	shared_ptr<Node<int>> sp1(new Node<int>(1));
	shared_ptr<Node<int>> sp2(new Node<int>(2));

	cout << "sp1.use_count:" << sp1.use_count() << endl;
	cout << "sp2.use_count:" << sp2.use_count() << endl;

	sp1->_pNext = sp2; //sp2的引用+1
	sp2->_pPre = sp1; //sp1的引用+1

	cout << "sp1.use_count:" << sp1.use_count() << endl;
	cout << "sp2.use_count:" << sp2.use_count() << endl;
}
int main()
{
	Funtest();
	system("pause");
	return 0;
}
//输出结果
//Node()
//Node()
//sp1.use_count:1
//sp2.use_count:1
//sp1.use_count:2
//sp2.use_count:2

从上面shared_ptr的实现中我们知道了只有当引用计数减减之后等于0,析构时才会释放对象,而上述情况造成了一个僵局,那就是析构对象时先析构sp2,可是由于sp2的空间sp1还在使用中,所以sp2.use_count减减之后为1,不释放,sp1也是相同的道理,由于sp1的空间sp2还在使用中,所以sp1.use_count减减之后为1,也不释放。sp1等着sp2先释放,sp2等着sp1先释放,二者互不相让,导致最终都没能释放,内存泄漏。

解决方案:使用weak_ptr指针,弱引用,只是引用,不计数。