目录
1 智能指针的原理
应用场景:为什么要使用智能指针:1-内存泄漏,随着代码的日趋复杂,很难保证内存被释放。2-多线程下的析构问题,对象一般是被多个线程使用。假如某个线程想要释放这个对象,但另外一个线程还在使用这个对象,可能会出现野指针问题。
原理:智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源
类型:unique_ptrunique_ptr是独占型智能指针。独占性,就是不允许多个智能指针指向同一块内存空间。也不支持拷贝、赋值,即不能通过赋值将一个unique_ptr赋值给另一个unique_ptr,将复制构造函数和赋值构造函数声明为delete就可以实现独占式,只允许移动构造和移动赋值。shared_ptr通过引用计数的方式解决智能指针的拷贝问题。每一个被管理的资源都有一个对应的引用计数,通过这个引用计数记录着当前有多少个对象在管理着这块资源。weak_ptr用来解决shared_ptr循环引用。auto_ptr前于以上三个,被淘汰了。
2.移动语义和完美转发
移动语义:移动语义是为了浅拷贝和深拷贝产生的,浅拷贝会出现两个对象占用一地址,会出现内存泄露。深拷贝开辟两个内存分别存放,造成资源浪费。移动语义则采用的是偷窃的方法,占为己有。为了保证移动语义, 必须记得用std::move 转化左值对象为右值,以避免调用复制构造函数.
万能引用:C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值在函数模板中的右值引用又被称为万能引用。
template <typename T>
void function(T&& t) {
otherdef(t);
}
引用折叠:当实参为左值或者左值引用(A&)时,函数模板中 T&& 将转变为 A&(A& && = A&);
当实参为右值或者右值引用(A&&)时,函数模板中 T&& 将转变为 A&&(A&& && = A&&)。
完美转发:无论调用 function() 函数模板时传递给参数 t 的是左值还是右值,对于函数内部的参数 t 来说,它有自己的名称,也可以获取它的存储地址,因此它永远都是左值,也就是说,传递给 otherdef() 函数的参数 t 永远都是左值。无论从那个角度看,function() 函数的定义都不“完美”。完美转发这样严苛的参数传递机制,C++98标准中几乎不会用到,但 C++11 标准为 C++ 引入了右值引用和移动语义,因此很多场景中是否实现完美转发,直接决定了该参数的传递过程使用的是拷贝语义(调用拷贝构造函数)还是移动语义(调用移动构造函数)。
3.lambda表达式
它自身的好处是可以到了需要函数对象的地方再写函数,而不是强制在外面提前写好。
4.继承和多态中final和overide关键词
final修饰类:被final修饰的类叫做最终类,最终类无法被继承。
final修饰虚函数:final修饰虚函数,表示该虚函数不能再被重写,如果子类继承后重写了该虚函数则编译报错。
override修饰虚函数:override修饰子类的虚函数,检查子类是否重写了父类的某个虚函数,如果没有没有重写则编译报错。
5.左值右值
众所周知C++11新增了右值引用,这里涉及到很多概念:
左值:可以取地址并且有名字的东西就是左值。
右值:不能取地址的没有名字的东西就是右值。
纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。
将亡值:可以理解为即将要销毁的值。
左值引用:对左值进行引用的类型。
右值引用:对右值进行引用的类型。
移动语义:转移资源所有权,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。
完美转发:可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。
返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。
6.auto & decltype
关于C++11新特性,最先提到的肯定是类型推导,C++11引入了auto和decltype关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。
auto:让编译器在编译器就推导出变量的类型,可以通过=右边的类型推导出变量的类型。
auto a = 10; // 10是int型,可以自动推导出a是int
decltype:相对于auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算。
cont int &i = 1;
int a = 2;
decltype(i) b = 2; // b是const int&
7.default
c++11引入default特性,多数时候用于声明构造函数为默认构造函数,如果类中有了自定义的构造函数,编译器就不会隐式生成默认构造函数,如下代码:
struct A {
int a;
A(int i) { a = i; }
};
int main() {
A a; // 编译出错
return 0;
}
上面代码编译出错,因为没有匹配的构造函数,因为编译器没有生成默认构造函数,而通过default,程序员只需在函数声明后加上“=default;
”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体,如下:
struct A {
A() = default;
int a;
A(int i) { a = i; }
};
int main() {
A a;
return 0;
}
编译通过。
8.const和constexptr
相同点:const和consexpr都是用来定义常量的。
不同点:const声明的常量,初始值引用的对象不一定是一个常量;constexpr声明的常量,初始值一定是常量表达式。