C++_类实例—指针-内存

发布时间 2023-12-05 20:07:21作者: 辰令

智能指针

 # g++ t.cpp -std=c++11
C++11中引入了智能指针的概念,方便管理堆内存
 智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,
  这使得智能指针实质是一个对象,行为表现的却像一个指针
包含在头文件<memory>中,shared_ptr、unique_ptr、weak_ptr  
 1.oop--在运行阶段决策--而不是编译阶段 
  数据类型--数据值--数据地址
   指针--存储的是值的地址  获取地址 &    --描述内存的表示法--》 十六进制标识	 
  视角一(自然视角--应用视角): 将值视为基础,地址视为派生量
  视角二: 将地址视为指定的量,将值视为派生量
         * 间接值或者解除引用运算符
		  指针描述的是位置--将两个地址相乘是没有任何意义的。因此不能简单的将整数赋给指针
		  C++ 允许将指针和整数相加,加1的结果是原来的地址值加上指向的对象占用的总字节数
 声明指针;初始化指针;
     声明指针的时候必须指定指针指向的数据类型
  C++ 在创建指针时,计算机将分配用来 存储地址的内存,但不会分配 用来存储指针所指向的数据的内存
  为数据提供空间是一个独立的步骤
  
  指针在运行阶段分配未命名的内存以存储值,在这种情况下只能通过指针来访问内存。使程序在管理内存方面有更大的控制权
   在运行阶段--new将找到一个长度正确的内存块。并返回该内存的地址。 程序员通过的做法是将该地址赋给一个指针
  
  C++将数组名解释为地址--数组名被解释为其第一个元素的地址,而对数组名应用地址运算符得到的是整个数组的地址
  
 C++提供了 引用语法,以期解决指针带来的问题。引用是变量的别名,一定有本体 引用声明时就必须初始化
           智能指针”	 

区别:

智能指针还有一个作用是把值语义转换成引用语义。C++和Java有一处最大的区别在于语义不同   
 在Java里面下列代码:
    Animal a = new Animal();

   Animal b = a;
Java里其实只生成了一个对象,a和b仅仅是把持对象的引用而已。
但在C++中不是这样,
Animal a;
Animal b = a;
这里却是就是生成了两个对象

类的实例化

类对象
   有两种类的成员变量:static和非static,有三种成员函数:static、非static和virtual
     类的成员变量相当于c的结构体,类的成员函数类似于c的函数,
	 类的静态变量类似于c的静态或全局变量,至于虚函数,函数体还是放在代码区,但虚函数的指针和成员变量一起放在数据区,这是因为虚函数的函数体有多个,不同的子类调用同一虚函数实则调用的不同函数体,因此需要在类的数据区保持真正的虚函数的指针

  new和delete是c++的运算符,malloc和free是C语言的标准库函数
在 C++ 中,类 是无法直接使用的,我们只有使用类创建了对象之后,使用对象来调用类中的成员属性和方法。
 使用类创建对象的过程,又称为类的实例化
// 显式创建并调用无参构造器
   CEmployee *cEmployee1 = new CEmployee; //显式new创建并调用无参构造器
   类 Student 的构造函数没有参数,因此,实例化类时不需要传入任何参数,并且不需要加括号。
//  显式new创建并调用无参构造器  显式new创建并调用参构造器--new 不只是分配了内存,它还创建了对象
    CEmployee cEmployee1 = CEmployee; //显式创建并调用无参构造器
//  隐式创建--直接使用类定义声明:
      CEmployee cEmployee1; //隐式创建并调用无参构造器
	  Student stu("HaiCoder", 109);
	  
 使用new 必须delete删除,new创建类对象使用完需delete销毁
 不用new 系统会自动回收内存

指针

C++ 中广泛用于三个主要用途:
    在堆上分配新对象,C++中的指针的一个典型用法就是管理一段内存
   将函数传递给其他函数
    循环访问数组或其他数据结构中的元素
const 关键字指定指针在初始化后无法修改

 new运算符初始化内存并将新分配和初始化的内存的地址返回给指针变量
unique_ptr 重载了->和*运算符,因此可以像使用普通指针那样使用 unique_ptr 智能指针	 

shared_ptr怎么用

 每一个shared_ptr对象在其内部都管理两部分内容:
   shared_ptr 对象本身
   shared_ptr 对象管理的内容
 引用计数机制-C++智能指针类 shared_ptr 提供了成员函数 use_count() 用于检查实际的“计数值”
 shared_ptr 类的构造函数是 Explicit 的
   raw 指针同时只能被一个 unique_ptr 指针绑定,我们不能拷贝 unique_ptr 对象,只能转移


 #include <memory>
   01.直接调用构造函数
   02. make_shared
 
01. std::shared_ptr<int> p1(new int());
    堆上分配了两块内存,一块用于存储new出来的 int 值,一块用于存储 shared_ptr 对象本身

 02.std::shared_ptr<int> p1 = std::make_shared<int>();
    std::make_shared执行了类似于 shared_ptr 构造函数类似的工作:在堆上分配两块内存,一块用于存储 int 值,一块用于存储 shared_ptr 对象本身
 
 shared_ptr 类提供的 reset() 成员函数即可:
 	 p1.reset();
	直接解绑 p1 绑定的指针 
	 p1 = nullptr;
 
 如果不使用 make_shared,则必须先使用显式 new 表达式来创建对象,然后才能将其传递到 shared_ptr 构造函数
 shared_ptr 在 C++ 标准库容器中很有用。 您可以将元素包装在 shared_ptr 中,然后将其复制到其他容器中(
 这意味着智能指针负责删除原始指针指定的内存。 智能指针析构函数包括要删除的调用,并且由于在堆栈上声明了智能指
 创建对象后让系统负责在正确的时间将其删除
 
  能够知道自己管理的对象是否还有人使用,若是没有人再使用自己管理的对象,就会自动的删除该对象
  shared”意味着共享,即不同的shared_ptr可以与同一个指针建立联系,其内部通过“引用计数机制”实现自动管理指针
    weak_ptr 提供对一个或多个 shared_ptr 实例所属对象的访问
  
  std::unique_ptr<int> ptr;
  std::unique_ptr<Task> taskPtr(new Task(23));
  // 通过智能指针访问成员变量
  int id = taskPtr->mId;
    std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);

普通指针

  int *p = new int(3); delete p;
  int *p2 = new int[12]; delete[] p;
 声明、初始化和使用原始指针。 它使用 new 初始化,以指向堆上分配的对象,必须显式删除 (delete) 该对象
 // 定义
 Coder* coder; 
 // 赋值
 coder = new Coder("cpp"); 
 // 读取
 coder->show();
 // 释放

指针

初始化-使用--生命周期-作用域
shared_ptr多个指针指向相同的对象
     make_shared 函数初始化	 
	 int b = 20;
	 std::shared_ptr<int> ptrb = std::make_shared<int>(b);
// 使用make_shared创建一个shared_ptr
std::shared_ptr<int> p1 = std::make_shared<int>();	 
	 
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,
shared_ptr 相对于普通指针的优缺点  缺少 ++, – – 和 [] 运算符 相互引用时导致内存泄漏(使用weak_ptr解决)
    它的最大作用在于协助shared_ptr工作	 
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象
   unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,
     如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权	 
C++11, make_shared使用变长参数模板, 
    auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");
	
	shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));

内存管理

 C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
    堆,就是那些由new分配的内存块 delete 堆区(heap): 存放malloc申请的内存,使用free释放
	栈来讲,是由编译器自动管理 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量。
	自由存储区:存放new申请的内容,使用delete
 全局区(静态区):存放全局变量和静态变量,初始化的全局变量和静态变量在一块区域,
 	 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。
	 常量区 :常量字符串就是放在这里的。 程序结束后由系统释放。

   基本内存布局:指针为代码部分提供了对堆内存的间接访问--》在堆栈部分创建一个指针,该指针指向堆部分的内存地址,从而间接访问堆部分
	 在堆部分分配内存发生在运行时,而不是在编译时,因此分配堆内存称为动态内存分配
	 

自动存储,静态存储,动态存储	
	 在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable)
	 
shared_ptr 真的能够万无一失的恰到好处地释放掉这些内存
  shared_ptr 内部默认的释放方法的确是 delete 方法,它不能应对各种复杂的指针
  shared_ptr 的构造函数能够接收两个参数,第一个参数常常是需要管理的指针,而第二个参数则可以是自定义的“指针释放方法 deleter”

文件

数据结构及函数的声明与实现进行分离的方式有两种:
   ①.h、.cpp;②.hpp。 
   在①中,.h用于数据结构及函数的声明;.cpp用于数据结构的初始化及函数的具体实现。 
   在②中,.hpp即负责数据结构及函数的声明,同时负责数据结构的初始化及其函数的具体实现。
hpp,Header plus plus的缩写,实质是将.cpp的实现代码混入.h头文件,即声明与定义(实现)都包含在同一个文件中
    适合用来编写公用的开源库
     *.hpp 场景中,由于定义与实现都已经存在于一个文件,调用者必需明确知道被调用者的所有定义,而不能等到cpp中去编译		

 dirty code temporary into hpp
  把dirty code 转变成 clean code 过程叫重构	

源码

  shared_ptr.h
  shared_ptr只是一个壳子,提供了外界的调用接口,真正的实现都在__shared_ptr基类中

 <functional>(lambda表达式)
 <memory>(智能指针)
###	  
  理想的硬件--CPU ,CPU Core直接对RAM进行读写 
    实际的CPU/RAM架构远比 的复杂
	
  编译器 

  现代C++的内存模型,便是为了屏蔽这些差异,而让你可以不用去了解特定平台特定编译器,也不用依赖互斥锁,就可以完成线程间的同步。
  C++11开始提供的std::atomic<>类模板,便可以作为更为底层的同步工具,这也是内存模型起作用的地方	  
  
  C++11共定义6种memory_order枚举值
 
 C++的内存模型 --指多线程编程方面
    内存模型是一种硬件上的概念,表示的是机器指令以什么样的顺序被处理器执行 
	  在线程中总是保持着顺序执行的特性,不会由于编译器或处理器对指令重排,从而导致原子变量的实际修改顺序与源代码的声明顺序不同。
	   我们称这样的特性为“顺序一致性
	    非原子类型不需要线程间同步
  C++对象的内存布局

 C++对象模型
 
 
 C程序中的内存布局
     C程序的内存布局包含五个段,
	   分别是STACK(栈段),HEAP(堆段),BSS(以符号开头的块),DS(数据段)和TEXT(文本段)。
	      每个段都有自己的读取,写入和可执行权限。如果程序尝试以不允许的方式访问内存,则会发生段错误,也就是我们常说的coredump

新技能

 Rust在嵌入式开发的体验
 05. 复合数据类型——指针  https://www.jianshu.com/p/a404a25964f3
 https://blog.popkx.com/
 C-智能指针shared_ptr怎样自定义释放函数-有几种方法-shared_ptr是怎么释放各种复杂的指针的呢
 C++11中的shared_ptr智能指针入门级介绍,及其相关简单C++程序实例
 https://blog.popkx.com/C-11%E4%B8%AD%E7%9A%84shared_ptr%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%85%A5%E9%97%A8%E7%BA%A7%E4%BB%8B%E7%BB%8D-%E5%8F%8A%E5%85%B6%E7%9B%B8%E5%85%B3%E7%AE%80%E5%8D%95C-%E7%A8%8B%E5%BA%8F%E5%AE%9E%E4%BE%8B/
 shared_ptr实现分析  https://juejin.cn/post/7099802726952402951
 https://codebrowser.dev/codebrowser/include/c++/11/memory.html
 https://codebrowser.dev/codebrowser/include/c++/11/bits/unique_ptr.h.html
 https://codebrowser.dev/codebrowser/include/c++/11/
 https://gcc.gnu.org/onlinedocs/gcc-4.6.0/libstdc++/api/a01033.html
 https://github.com/gcc-mirror/gcc/tree/master/libstdc%2B%2B-v3/include/bits