C++ primer 第十二章 复习(1)

简介: C++ primer 第十二章 复习

12.1.1 动态内存与智能指针

全局对象:在程序启动时分配,程序结束时销毁


局部对象:进入其作用域时被创建,离开作用域销毁(栈对象)


静态对象:在第一次使用之前分配,程序结束销毁


动态内存和智能指针


动态内存(堆)的管理是通过一对运算符来完成的


new:在动态内存中为对象分配空间并返回一个指向该对象的指针


delete:接受一个动态内存的指针,销毁该对象,释放相关内存


为了更安全的使用内存,C++11提供了两个智能指针


shared_ptr:允许多个指针指向同一个对象


unique_ptr:独占对象


和普通指针类似,区别在于它负责自动释放所指向的对象


#include<list>#include<string>#include<memory>//智能指针也是模板,默认初始化的智能指针中保存着一个空指针
std::shared_ptr<std::string> p1;//可指向string
std::shared_ptr<std::list<int>> p2; //可指向 指向int的list(即std::list<int>)
//如果p1不为空,检查它是否可以指向一个空字符串
if (p1&&p1->empty()){
  *p1 ="HI";//解引用p1,将HI赋予其指向的对象
}

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数


//指向一个值为42的int shared_ptr
std::shared_ptr<int> p3 = std::make_shared<int>(42);
//指向一个值为 "9999999999" 的string
std::shared_ptr<std::string> p4 = std::make_shared<std::string>(10,'9');
//指向一个值初始化的int,即值为0
std::shared_ptr<int> p5 = std::make_shared<int>();
//指向一个动态分配的空std::vector<std::string>
auto p6 = std::make_shared<std::vector<std::string>>();
auto r = std::make_shared<int>(42);//r指向的int只有一个引用者
r = q;//给r赋值,令它指向另一个地址
//递增q的引用计数,递减r原来的引用计数,r原来指向的对象没有引用者,会自动释放
//factory返回一个shared_ptr,指向一个动态分配内存的对象
std::shared_ptr<FOO> factory(T arg){
    return std::shared_ptr<FOO>(arg);
}
void use_factory(T arg){
    std::shared_ptr<FOO> p = factory(arg);
}//离开作用域时, arg对象的内存将被自动释放

使用动态内存的一个常见原因:多个对象共享相同的状态


12.1.2 shared_ptr 和 new 结合使用

直接管理内存


默认初始化


如果定义变量没有被初始化,则变量被赋予默认值


默认值由变量类型决定,且和变量定义位置有关


内置类型,函数体之外被初始化为 0


每个类决定其初始化对象的方式(string 空字符串)


int* pi = new int; //指向一个动态分配的,未初始化的无名对象
string* ps= new string; //初始化空string

直接初始化


string* ps1 = new string; //默认初始化空string
string* ps2 = new string(); //值初始化为空string
int* pi1 = new int; //默认初始化: 其值未定义
int* pi2 = new int(); //值初始化为 0

使用 auto 从初始化器来推断分配的对象类型


auto p1 = new auto(obj); //p1指向一个与 obj 类型相同的对象,该对象使用 obj 初始化
auto p1 = new auto(a,b,c);  //错误:括号中只能有单个初始化器

用 new 分配 const 对象是合法的


//分配并初始化一个 const int
const int* pci = new const int(1024);
//分配并默认初始化一个 const 的空 string
const string *pcs = new const string;

内存耗尽


int* p1 = new int; //分配失败,则new 抛出std::bad_alloc
int* p1 = new(nothrow); //分配失败,则返回一个空指针
//定位 new 表达式,nothrow 对象告诉它不能抛出异常

指针和 delete


int i, *pil = &i, *pi2 = nullptr;
double *pd = new double(33),*pd2 = pd;
delete i; //错误
delete pil; //错误,非动态内存的指针(没有new也不需要delete)
delete pd; //正确
delete pd2; //错误,pd2指向的内存已经被释放了
delete pi2; //正确释放一个空指针没有错
const int* pci = new const int(1024);
delete pci; //正确,释放一个const对象

在 delete 之后,指针就变成了空悬指针


int *p(new int(42)); //p指向动态内存
auto q = p; //p 和 q 指向相同的内存
delete p; //p 和 q 均变为无效
p = nullptr; //p不再绑定到任何对象
//q依赖是空悬指针

shared_ptr 和 new 结合使用


std::shared_ptr<double> p1; 
std::shared_ptr<int> p2(new int(42)); 

智能指针的构造函数是 explicit 的


std::shared_ptr<int> p1 = new int(1024); //错误,必须直接初始化 
std::shared_ptr<int> p2(new int(42));  //正确
std::shared_ptr<int> clone(int p){
  return std::shared_ptr<int>(new int(p))
}


不要混合使用普通指针和智能指针


因为普通指针指向的对象将会被智能指针销毁时释放


std::shared_ptr<int> p (new int(42)); //引用计数为1
process(p); //拷贝p会递增它的引用计数,在process中引用计数为2
int i = *p; //正确,引用计数为1
//用内置指针显式构造一个shared_ptr,这样做会导致错误
int* x(new int(1024)); //x是一个普通指针
process(x); //错误,不能将 int* 转换为 shared_ptr<int>
process(std::shared_ptr<int>(x)); //合法,但是内存将会被释放
int j = *x; //j未定义,因为 x 指向的内存已经被释放,x是空悬指针

get : 向不能使用智能指针的代码,传递一个内置指针


永远不要用 get 初始化一个智能指针或为另一个智能指针赋值


std::shared_ptr<int> p(new int(42)); //引用计数为1
int* q = p.get(); //正确,但使用q的时候要注意,不要让其管理的指针释放
{ //新程序块
//未定义:两个独立的shared_ptr指向相同的内存
  std::shared_ptr<int>(q);
}
int foo = *p //未定义的,p指向的内存已经被释放了

reset:更新引用计数,会释放 p 指向的对象


std::shared_ptr<int> p = new int(42); //错误,不能将一个指针赋予 shared_ptr
std::shared_ptr<int> p;
p.reset(new int(42)); //正确,p指向一个新对象
//-------------------------------------------------
std::shared_ptr<std::string> p;
if (!p.unique()){ //p.use_count为1,返回true,否则返回false
  p.reset(new std::string(*p)); //不是唯一使用者,分配新的拷贝给智能指针
}
*p += newVal; //是唯一对象的使用者,可直接改变对象的值

智能指针和异常


/*
  使用智能指针,即使程序块过早结束,也能正确释放内存
*/
void f(){
    std::shared_ptr<int> sp(new int(42)); //分配一个新对象
    //这段代码抛出一个异常,且 f()函数未捕获
}//在函数结束时,智能指针会自动释放内存
void f(){
    int *ip = new int(42); //动态分配对象
     //这段代码抛出一个异常,且 f()函数未捕获
    delete ip; //在退出前释放内存,这段释放内存的操作并未被执行
}

使用类似的技术来管理不具有良好定义的析构函数的类






相关文章
|
5月前
|
编译器 C++
c++primer plus 6 读书笔记 第十章 对象和类
c++primer plus 6 读书笔记 第十章 对象和类
|
5月前
|
编译器 数据安全/隐私保护 C++
c++primer plus 6 读书笔记 第十三章 类继承
c++primer plus 6 读书笔记 第十三章 类继承
|
5月前
|
C++
C++ Primer Plus (第6版)中文版 (使用XMind整理)
C++ Primer Plus (第6版)中文版 (使用XMind整理)
C++ Primer Plus (第6版)中文版 (使用XMind整理)
|
5月前
|
C++
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
|
5月前
|
C++
c++primer plus 6 读书笔记 第十一章 使用类
c++primer plus 6 读书笔记 第十一章 使用类
|
5月前
|
编译器 C++
c++primer plus 6 读书笔记 第八章 函数探幽0
c++primer plus 6 读书笔记 第八章 函数探幽0
|
5月前
|
编译器 vr&ar C++
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
|
5月前
|
SQL 人工智能 算法
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
|
5月前
|
程序员 C++
c++primer plus 6 读书笔记 第十二章 类和动态内存分配
c++primer plus 6 读书笔记 第十二章 类和动态内存分配
|
5月前
|
存储 IDE 编译器
c++primer plus 6 读书笔记 第九章 内存模型和名称空间
c++primer plus 6 读书笔记 第九章 内存模型和名称空间