C++11常用知识

Posted Arrow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11常用知识相关的知识,希望对你有一定的参考价值。

1. 简介

2. C++的结构体和C++类的区别

  • 概念
    • class和struct的语法基本相同,从声明到使用,都很相似,但是struct的约束要比class多,理论上,struct能做到的class都能做到,但class能做到的stuct却不一定做的到
    • C++结构体的继承默认是public,而c++类的继承默认是private
    • class还可以用于表示模板类型,struct则不行
  • 类型:struct是值类型,class是引用类型,因此它们具有所有值类型和引用类型之间的差异
  • 效率:由于堆栈的执行效率要比堆的执行效率高,但是堆栈资源却很有限,不适合处理逻辑复杂的大对象,因此struct常用来处理作为基类型对待的小对象,而class来处理某个商业逻辑
  • 关系:struct不仅能继承也能被继承 ,而且可以实现接口,不过Class可以完全扩展。内部结构有区别,struct只能添加带参的构造函数,不能使用abstract和protected等修饰符,不能初始化实例字段
  • 性能:关于 Class性能好还是Struct性能好(换言堆性能好?还是栈性能好?) 那么什么时机该用呢 ,比较struct和 class的不同,以下总结可以参考一下:
    • 在表示诸如点、矩形等主要用来存储数据的轻量级对象时,首选struct。
    • 在表示数据量大、逻辑复杂的大对象时,首选class
    • 在表现抽象和多级别的对象层次时,class是最佳选择

3. 智能指针

  • 为什么引入智能指针
    • 在实际的 C++ 开发中,我们经常会遇到诸如程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当造成的。比如:
    • 有些内存资源已经被释放,但指向它的指针并没有改变指向(成为了野指针),并且后续还在使用;
    • 有些内存资源已经被释放,后期又试图再释放一次(重复释放同一块内存会导致程序运行崩溃);
    • 没有及时释放不再使用的内存资源,造成内存泄漏,程序占用的内存资源越来越多。
  • 什么是智能指针
    • 所谓智能指针,可以从字面上理解为“智能”的指针
    • 具体来讲,智能指针和普通指针的用法是相似的,不同之处在于,智能指针可以在适当时机自动释放分配的内存
    • 也就是说,使用智能指针可以很好地避免“忘记释放内存而导致内存泄漏”问题出现

3.1 C++11 std::shared_ptr智能指针

  • 与其它智能指针的区别
    • 与unique_ptr、weak_ptr 不同之处在于,多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)

3.1.1 std::shared_ptr智能指针的创建

  • 构造出std::shared_ptr 类型的智能指针
std::shared_ptr<int> p1;             //不传入任何实参
std::shared_ptr<int> p2(nullptr);    //传入空指针 nullptr
  • 注意,空的 shared_ptr 指针,其初始引用计数为 0,而不是 1
  • 构造一个指向一块存有 10 这个 int 类型数据的堆内存空间的智能指针
std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p3 = std::make_shared<int>(10);
//以上 2 种方式创建的 p3 是完全相同
  • shared_ptr 模板提供相应的拷贝构造函数和移动构造函数
//调用拷贝构造函数:p4 和 p3 指向同一块堆内存,同时该堆空间的引用计数会加 1
std::shared_ptr<int> p4(p3);//或者 std::shared_ptr<int> p4 = p3;

//调用移动构造函数:std::move(p4) 初始化 p5,会使得 p5 拥有了 p4 的堆内存,而 p4 则变成了空智能指针
std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);
  • 注意,同一普通指针不能同时为多个 shared_ptr 对象赋值,否则会导致程序发生异常
int* ptr = new int;
std::shared_ptr<int> p1(ptr);
std::shared_ptr<int> p2(ptr);//错误

3.1.2 std::shared_ptr成员函数

成员方法名功 能
operator=()重载赋值号,使得同一类型的 shared_ptr 智能指针可以相互赋值
operator*()重载 * 号,获取当前 shared_ptr 智能指针对象指向的数据
operator->()重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员
swap()交换 2 个相同类型 shared_ptr 智能指针的内容
reset()当函数没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为 1
get()获得 shared_ptr 对象内部包含的普通指针
use_count()返回同当前 shared_ptr 对象(包括它)指向相同的所有 shared_ptr 对象的数量
unique()判断当前 shared_ptr 对象指向的堆内存,是否不再有其它 shared_ptr 对象再指向它
operator bool()判断当前 shared_ptr 对象是否为空智能指针,如果是空指针,返回 false;反之,返回 true

3.2 std::unique_ptr智能指针

  • 与其它智能指针的区别
    • 与 std::shared_ptr 指针最大的不同之处在于,unique_ptr 指针指向的堆内存无法同其它 unique_ptr 共享,也就是说,每个 unique_ptr 指针都独自拥有对其所指堆内存空间的所有权
    • 作为智能指针的一种,unique_ptr 指针自然也具备“在适当时机自动释放堆内存空间”的能力

3.2.1 std::unique_ptr智能指针的创建

  • 通过以下 2 种方式,可以创建出空的 unique_ptr 指针:
std::unique_ptr<int> p1();
std::unique_ptr<int> p2(nullptr);
  • 创建 unique_ptr 指针的同时,也可以明确其指向
std::unique_ptr<int> p3(new int);
  • 基于 unique_ptr 类型指针不共享各自拥有的堆内存,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供了移动构造函数
std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(p4);//错误,堆内存不共享
std::unique_ptr<int> p5(std::move(p4));//正确,调用移动构造函数,p5 将获取 p4 所指堆空间的所有权,而 p4 将变成空指针(nullptr) 

3.2.2 std::unique_ptr成员函数

成员方法名功 能
operator*()获取当前 unique_ptr 指针指向的数据
operator->()重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员
operator =()重载了 = 赋值号,从而可以将 nullptr 或者一个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针
operator 重载了 [] 运算符,当 unique_ptr 指针指向一个数组时,可以直接通过 [] 获取指定下标位置处的数据
get()获取当前 unique_ptr 指针内部包含的普通指针
get_deleter()获取当前 unique_ptr 指针释放堆内存空间所用的规则
operator bool()unique_ptr 指针可直接作为 if 语句的判断条件,以判断该指针是否为空,如果为空,则为 false;反之为 true
release()释放当前 unique_ptr 指针对所指堆内存的所有权,但该存储空间并不会被销毁
reset§其中 p 表示一个普通指针,如果 p 为 nullptr,则当前 unique_ptr 也变成空指针;反之,则该函数会释放当前 unique_ptr 指针指向的堆内存(如果有),然后获取 p 所指堆内存的所有权(p 为 nullptr)
swap(x)交换当前 unique_ptr 指针和同类型的 x 指针

3.3 std::weak_ptr智能指针

  • weak_ptr 模板类中没有重载 * 和 -> 运算符,这也就意味着,weak_ptr 类型指针只能访问所指的堆内存,而无法修改它
  • 与其它智能指针的关系
    • C++11标准虽然将 weak_ptr 定位为智能指针的一种,但该类型指针通常不单独使用(没有实际用处),只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具,借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。
    • 需要注意的是,当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数

3.3.1 std::weak_ptr指针的创建

  • 可以创建一个空 weak_ptr 指针
std::weak_ptr<int> wp1;
  • 凭借已有的 weak_ptr 指针,可以创建一个新的 weak_ptr 指针
// 若 wp1 为空指针,则 wp2 也为空指针;反之,如果 wp1 指向某一 shared_ptr 指针拥有的堆内存,
// 则 wp2 也指向该块存储空间(可以访问,但无所有权)
std::weak_ptr<int> wp2 (wp1);
  • weak_ptr 指针更常用于指向某一 shared_ptr 指针拥有的堆内存,因为在构建 weak_ptr 指针对象时,可以利用已有的 shared_ptr 指针为其初始化
// wp3 指针和 sp 指针有相同的指针。再次强调,weak_ptr 类型指针不会导致堆内存空间的引用计数增加或减少
std::shared_ptr<int> sp (new int);
std::weak_ptr<int> wp3 (sp);

3.3.2 std::weak_ptr成员函数

成员方法名功 能
operator=()重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值
swap(x)其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容
reset()将当前 weak_ptr 指针置为空指针
use_count()查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量
expired()判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)
lock()如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针

4. std::enable_shared_from_this

  • 功能
    • 如果一个T类型的对象t,是被std::shared_ptr管理的,且类型T继承自std::enable_shared_from_this,那么T就有个shared_from_this的成员函数,这个函数返回一个新的std::shared_ptr的对象,也指向对象t
  • 应用场景
    • 当类T被share_ptr管理,且在类T的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr
    • 一个主要的场景是保证异步回调函数中操作的对象仍然有效
// 错误地使用this指针
class Foo {
public:
   void Bar(std::function<void(Foo*)> fnCallback) {
      // async call fnCallback with this
   }
}
// Foo::Bar接受一个函数对象,这个对象需要一个Foo*指针,其实要的就是Foo::Bar的this指针,但是这个回调是异步的,
// 也就是说可能在调用这个回调函数时,this指向的Foo对象已经提前析构了。
// 这时候,std::enable_shared_from_this就派上用场了
// 正确的使用方法
class Foo : public std::enable_shared_from_this<Foo> {
public:
   void Bar(std::function<void(std::shared_ptr<Foo>)> fnCallback) {
      std::shared_ptr<Foo> pFoo = shared_from_this();
      // async call fnCallback with pFoo
   }
}
// 这样就可以保证异步回调时,Foo对象仍然有效
 - 

以上是关于C++11常用知识的主要内容,如果未能解决你的问题,请参考以下文章

C++11常用知识点(上)

C++11常用知识点(下)

39. C++11常用知识点总结

那些你常用的JSP知识

以下代码片段 C++ 的说明

C++11特性之std:call_once介绍