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

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


12.1.3 unique_ptr 和 weak_ptr

unique_ptr : 独自拥有所指向的对象


std::unique_ptr<double> p1; //可以指向一个double的unique_ptr
std::unique_ptr<int> p2(new int(42)); //p2指向一个值为42的int
std::unique_ptr<std::string> p1(new std::string("HI"));
std::unique_ptr<std::string> p2(p1); //错误:unique_ptr不支持拷贝
std::unique_ptr<std::string> p3 = p1; //错误:unique_ptr不支持拷贝


unique_ptr 虽然不能拷贝或赋值 ,但可以通过 release 或 reset 将指针所有权从一个(非const)的转移到另一个


std::unique_ptr<std::string> p1(new std::string("HI"));
std::unique_ptr<std::string> p2(p1.release()); //release将p1置为空
std::unique_ptr<std::string> p3(new std::string("HI1"));
//将所有权从p3转移到p2
p2.reset(p3.release()); //释放原来指向的内存

不能拷贝的例外 : 可以拷贝或赋值一个将要销毁的 unique_ptr


std::unique_ptr<int> clone(int p){
  //正确: 从int* 创建一个 unique_ptr
  return std::unique_ptr<int>(new int(p));
}
std::unique_ptr<int> clone(int p){
  std::unique_ptr<int> ret(new int(p));
  return ret;
}

重写默认删除器


动态内存与智能指针


weak_ptr :绑定到 shared_ptr ,不会改变引用计数



auto p = make_shared<int>(42); 
weak_ptr<int> wp(p); //wp弱共享p,p的引用计数并未改变
//不能使用 weak_ptr 直接访问对象,必须调用 lock
if(shared_ptr<int> np = wp.lock()){ //如果np不为空
    //在if中, np 和 p 共享对象
}

展示 weak_ptr 用途


检查指针类:将为StrBlob 类定义一个伴随指针类



//对于访问一个不存在元素的尝试,StrBlobPtr抛出一个异常
class StrBlobPtr{
public:
    StrBlobPtr():curr(0){};
    StrBlobPtr(StrBlob & a,size_t sz =0):wptr(a.data),curr(sz){};
    string& deref() const;
    StrBlobPtr& incr(); //前缀递增
private:
    //若检查成功,check返回一个指向 vector 的 shared_ptr
    shared_ptr<vector<string>> check(size_t,const string&) const;
    //保存一个 weak_ptr,因为底层 vector 可能会被销毁
    weak_ptr<vector<string>> wptr;
    size_t curr;//在数组当前的位置
}
shared_ptr<vector<string>> StrBlobPtr::check(size_t i,const string& msg) const{
    auto ret = wptr.lock(); //vector还存在吗?
if(!ret) throw runtime_err("unbound StrBlobPtr");
if(i>= ret->size()) throw out_of_range(msg);
    return ret; //否则,返回指向vector的shared_ptr<vector<string>>
}
string& StrBlobPtr::deref() const{
  auto p = check(curr,"derefernce past end");
  return (*p)(curr); //*p是对象所指的vector
}
StrBlobPtr& StrBlobPtr::incr(){
  //如果curr已经指向容器的尾后,就不能递增了
  check(curr,"increment past end of StrBlobPtr");
++curr;
    return *this;
}
//对于友元声明来说,前置声明是必要的
class StrBlobPtr;
class StrBlob{
  friend class StrBlobPtr;
  //...
  //返回指向首元素和尾后元素的StrBlobPtr
  StrBlobPtr begin(){return StrBlobPtr(*this)};
  StrBlobPtr end(){
      auto ret =  StrBlobPtr(*this,data->size());    
      return ret;
  };
};

weak_ptr 打断 shared_ptr 的循环引用


std::weak_ptr :打断 std::shared_ptr 所管理的对象组成的环状引用。(打破shared_ptr的循环引用)


若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。


能令环中的指针之一为弱指针以避免此情况


#include <iostream>#include <memory>using namespace std;
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
class Child
{
public:
    ParentPtr father;
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};
typedef std::shared_ptr<Child> ChildPtr;
class Parent {
public:
    ChildPtr son;
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
};
void testParentAndChild()
{
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;
    c->father = p; 
}
int main()
{
    testParentAndChild();
    return 0;
}

一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效

#include <iostream>#include <cassert>#include <memory>using namespace std;
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;
class Child
{
public:
    WeakParentPtr father;                 // 只要一环换成 weak_ptr, 即可打破环 
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};
typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;
class Parent {
public:
    ChildPtr son;                 
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
};
void testParentAndChild()
{
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;            
    c->father = p;        
    cout << (c->father).use_count() << endl;
    cout << (p->son).use_count() << endl;
}
int main()
{
    testParentAndChild();
    return 0;
}

12.2 动态数组

new 和数组


动态数组并不是数组类型,分配一个数组将会得到一个元素类型的指针


//通过get_size确定分配多少个int
int* pia = new int[get_size()]; //pia指向第一个int
//方括号中的大小必须是整数,但不必是常量
typedef int arrT[42]; //arrT表示42个int的数组类型
int *p = new arrT; //分配一个 42个int的数组,p 指向第一个int
//编译器将会执行 int *p = new int[42]; 

初始化动态分配对象的数组


int* pia1 = new int[10]; //10个未初始化的int
int* pia2 = new int[10](); //10个初始化为0的int
string* psa1 = new string[10]; //10个空字符串
string* psa2 = new string[10](); //10个空字符串
//3个int分别用列表中对应的初始化器初始化
int* pia3 = new int[3]{0,1,2};
//前4个初始化器初始化,剩余进行值初始化
string* psa3 = new string[10]{"A","B","C",string(3,'x')};

动态分配一个空数组是合法的


size_t n = get_size(); //get_size返回需要的元素数目,可以为0
int* p = new int[n]; //分配数组保存元素
for(int *q=p;q!=p+n;++q){
    //处理数组
}
char arr[0];//错误,不能定义长度为0的数组
char cp= new char[0]; //正确,但cp不能解引用

释放动态数组


delete p; //p必须指向一个动态分配的对象,或者为空
delete p[]; //pa必须指向一个动态分配的数组或为空
//数组中的元素按逆序销毁

智能指针和动态数组


//标准库提供了一个可以管理new分配数组的unique_ptr版本
unique_ptr<int[]> up(new int[10]);
up.release(); //自动销毁其管理的指针
for(size_t i=0;i!=10;++i){
  up[i] = i; //为每个元素赋值
}


shared_ptr 不直接支持管理动态数组


//如果希望使用 shared_ptr 管理,必须自定义删除器
shared_ptr<int> sp(new int[],[](int *p){delete[] p;}); 
sp.reset(); //使用提供的 lambda 释放数组,它使用 delete[]
//shared_ptr未定义下标运算符,并且不支持指针的算术运算
for(size_t i=0; i!=10; ++i){
    *(sp.get() +i) = i; //使用 get 获取一个内置指针
}

Allocator 类

将内存分配和对象构造分离


分配大块内存,但只在真正需要时才执行指向对象创建操作


//将内存分配和对象构造组合在一起可能导致不必要的浪费
string* const p = new string[n]; //构造 n 个空 string
string s;
string *q = p; //q指向第一个string
while( cin>>s && q != p+n ){
    *q++= s; //赋予*q一个新值
}
const size_t size = q-p; //记录读取多少个string
//使用数组
delete[] p;  

1、可能不需要 n 个 string


2、每个对象都赋予了两遍值


3、没有默认构造函数的类就不能动态分配数组了



Allocator 类分配的内存是原始的,未构造的


allcator<string> alloc; //可分配 string 的 allocator 对象
//分配一段原始的,未构造的内存
auto const p = alloc.allocate(n); //分配 n 个未初始化的string
auto q = p; //q指向最后构造的元素之后的位置
//在q指向的内存中构造一个对象
alloc.construct(q++);
alloc.construct(q++,10,'c');
alloc.construct(q++,"HI");
cout << *p << endl; //正确,使用string的输出运算符
cout << *q << endl; //错误,q指向未构造的内存
while(q != p){
    alloc.destroy(--q); //释放构造的string
    //只能对真正构造了的元素进行destroy操作
}
//先释放对象,再释放内存
alloc.deallocated(p,n);释放内存

标准库为 allocator 提供了两个伴随算法,可以在未初始化内存中创建对象



假定有一个 int 的 vector ,希望将其拷贝到动态内存中


//分别比 vi 中元素所占空间大一倍的动态内存
auto p = alloc.allocate(v.size()*2);
//通过拷贝 vi 中的元素来构造从 p 开始的元素
auto q = uninitialized_copy(v.begin(),v.end(),p);
//前两个参数表示输入序列,第三个参数为目的地空间
//与copy不同,uninitialized_copy在给定的位置上构造元素
//q指向最后一个构造的元素之后的位置
//将剩余的元素初始化为42
uninitialized_fill_n(q,v.size(),42);
相关文章
|
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 读书笔记 第九章 内存模型和名称空间