C++Primer 第十二章

时间:2023-12-20 19:56:56
//1.标准库提供了两种智能指针类型来管理动态对象,均定义在头文件memory中,声明在std命名空间。
// shared_ptr:允许多个指针指向同一个对象。
// unique_ptr:独占所指的对象。
// 标准库还定义了weak_ptr的伴随类,它是一种弱作用。指向shared_ptr所管理的对象。 //2.shared_ptr和unique_ptr均支持的操作:
shared_ptr<T> sp
unique_ptr<T> up:空智能指针,指向类型为T的对象
p :将p用作一个判断条件,若p指向一个对象,则为true
*p, p->men :得到p所指向的对象
p.get() :返回p中保存的指针。
swap(p, q)
p.swap(q) :交换p和q的指针 //3.shared_ptr独有的操作:
make_shared<T>(args) :返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象。是安全的分配和使用动态内存的方法。其本身也是一个模板。
shared_ptr<T> p(q) :p是q的拷贝,此操作会递增q的计数器,q中的指针必须能转为T。
p = q :p和q都是shared_ptr,p的指针必须能指向q所指的对象,此操作会递减p的引用计数,递增q的引用计数。当p的引用计数变为0的时候则释放其管理的内存。
shared_ptr<const int> pInt(new int());
shared_ptr<int> pInt1;
pInt1 = pInt; //错误
pInt = pInt1; //正确
p.unique() :若p的引用计数为1,则返回true,否则返回false
p.use_count() :可能很慢,主要用于调试,返回与p共享对象的智能指针的数量 //4.当在容器中使用了shared_ptr,当不在需要容器中的全部元素的时候,记得用erase删除不再需要的元素。 //5.new和delete:
.在*空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。
.默认情况下,动态分配的对象是默认初始化的。
.可以采用直接初始化或值初始化的方式,方法是使用()。直接初始化:int *pValue = new int(); 值初始化:int *pValue = new int();
.默认情况下,new失败时候会抛出bad_alloc异常。使用定位new可以避免抛出异常:int *pValue = new (nothrow)int;当分配内存失败的时候,pValue将是一个空指针。nothrow定义在头文件new中,声明在命名空间std中。
.传递给delete的指针必须是动态分配的指针或者是空指针。多次释放相同的指针的行为是未定义的。
.在使用了delete的时候,最好将被操作的指针置空。 //6.可以用new返回的指针来直接初始化shared_ptr,此构造函数是explicit的,所以只能采用直接初始化的方式。 //7.不要用智能指针的get()函数得到的指针去初始化另一个智能指针,否则会导致多次delete同一块内存。
// shared_ptr类的reset成员函数可以将一个新的指针赋予shared_ptr,如果需要的话会释放其原来指向的内存。
// shared_ptr类默认使用delete来充当删除器。可以为shared_ptr对象指定自己的删除器。 //8.智能指针的使用规范:
// A:不使用相同的内置指针初始化或reset多个智能指针
// B:不delete get()返回的指针
// C:不使用get()初始化或reset另一个智能指针
// D:在使用get()返回的指针的时候,要记住当对应的最后一个智能指针销毁后,这个指针就无效了。
// E:在使用shared_ptr的时候,当管理的资源不是new分配的时候,要传递给其一个删除器。 //9.unique_ptr:其有一个可以接受内置指针的构造函数,是explicit的,所以当使用内置指针初始化unique_ptr的时候要采用直接初始化。 //10.unique_ptr的操作:
u = nullptr :将对象置空并释放其资源
u.release() :u放弃对指针的控制权,返回指针并且将u置空
u.reset() :释放u指向的对象,并将u置空
u.reset(q) :释放u指向的对象,并将u指向内置指针q
u.reset(nullptr):释放u指向的对象,并将u置空
// 虽然不能拷贝或赋值unique_ptr,但是可以通过release和reset来将指针的控制权由一个unique(非const)转移到另一个unique: p.reset(p1.release()); //11.不能拷贝或赋值unique_ptr的规则有一个例外:可以拷贝或赋值一个将要被销毁的unique_ptr:比如从函数返回一个unique_ptr,返回局部unique_ptr对象。
unique_ptr<int> fun() {unique_ptr<int> p(new int()); return p;}
unique_ptr<int> p = fun();//p = unique_ptr 10 //12.weak_ptr是一种不控制指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr上不会影响后者的使用计数。
w = p :p可以是一个shared_ptr对象或者是weak_ptr对象。赋值后w将共享p的对象
w.reset() :将w置空
w.use_count():与w共享对象的shared_ptr的数量
w.expired() :若w.use_count()为0,则返回true,否则返回false expire[ex·pire || ɪk'spaɪə]v.期满,断气,
w.lock() :如果w.expired()为true,返回一个空的shared_ptr,否则返回一个指向w的对象的shared_ptr。
// 由于对象可能不存在,所以weak_ptr不能直接访问对象,而要通过lock()函数。 //13.分配动态数组内存的时候,分配的大小为0也是允许的,但是不能对此指针进行解引用(vs2010下可以对其解引用,但是最好别用)。对于动态数组不能应用begin()和end()。要搭配使用delete[]。
// delete[]是按照逆序释放内存的。分配动态数组的时候可以使用值初始化,但是不能给出初始化器(这个特性导致无法使用new[]来为没有默认构造函数的类型分配动态数组)。int *p = new int[10]();是可以的。int *p = new int[10](0);是不可以的。 //14.使用unique_ptr可以管理一个动态数组,必须在类型后跟一对尖括号:unique_ptr<int []> pInt(new int[10]());在销毁pInt的时候,会自动使用delete[];
// 与unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理动态数组,必须提供自定义的删除器。shared_ptr<int> pInt(new int[10](), [](int *p){delete []p;});
// unique_ptr支持直接管理动态数组,其提供了下标运算符。对应的,shared_ptr不支持直接管理动态数组,其不支持下标运算符,只能通过其get()函数得到对象的指针,通过指针偏移的方式去访问动态数组中的对象。 //15.当分配一大块内存的时候,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离,这意味着我们可以分配大块内存,但是只在真正需要时才真正执行对象创建操作。
// 标准库allocator类定义在头文件memory中,声明在命名空间std中。它可以将内存分配和对象构造分离开来。其本身是一个类模板,会根据给定的对象类型来确定恰当的内存大小和对齐位置。
class CTest
{
public:
CTest(int i) : value(i){}
public:
int value;
};
allocator<CTest> allocTest;
CTest *p = allocTest.allocate(); //分配一段原始的未经构造的内存。指针p所指向40字节的内存是未经构造的
int value = p[].value; //value = -842150451。还未构造对象的情况下使用原始内存是错误的,虽然vs2010不报错,但是也不要这样做。
allocTest.construct(p, CTest()); //构造第一个对象,调用对象的构造函数
allocTest.destroy(p); //调用对象的析构函数,一旦元素被销毁,就可以在这块内存上保存其他的指定对象。
allocTest.deallocate(p, ); //释放从p开始的内存,从p开始的位置保存了10个CTest类型的对象。这个10是之前调用allocate时指定的。在调用此函数前,必须对此内存块中已经构造的对象调用destroy(); //16.uninitialized_copy(beg1, end1, beg2)
// uninitialized_copy_n(beg1, n, beg2):从输入迭代器指定的范围拷贝元素到迭代器beg2指定的未构造的原始内存中。此函数返回值为beg2中最后一个被构造的元素的尾后迭代器。
// uninitialized_fill(beg1, end1, t)
// uninitialized_fill_n(beg1, n, t):在由前两个参数指定的序列中,创建对应数目的对象,并用t来进行初始化。
allocator<CTest> allocTest;
CTest *pTest = allocTest.allocate();
vector<CTest> vecTest;
for(int i = ; i < ; ++i) {vecTest.emplace_back(i);}
auto pTem = uninitialized_copy(vecTest.begin(), vecTest.end(), pTest);
uninitialized_fill(pTem, pTem + , );
//pTest指向的内存中对象的值:0到9,10个1 allocator<unique_ptr<int>> allocTest;
auto *pTest = allocTest.allocate();
uninitialized_fill(pTest, pTest + , new int());
int sum = ;
for(int i = ; i < ; ++i){sum += *(pTest[i]);} //sum = 20; //17

 void Fun0(CRITICAL_SECTION* p)
 {
   LeaveCriticalSection(p);
 }

 auto FunTest = [](CRITICAL_SECTION* p){LeaveCriticalSection(p);};
 CRITICAL_SECTION Cs;
 shared_ptr<CRITICAL_SECTION> pShareCs(&Cs, [](CRITICAL_SECTION* p){LeaveCriticalSection(p);});
 unique_ptr<CRITICAL_SECTION, decltype(Fun0)*> pUniqueCs0(&Cs, Fun0);
 unique_ptr<CRITICAL_SECTION, decltype(FunTest)> pUniqueCs1(&Cs, FunTest);
 //unique_ptr<CRITICAL_SECTION, decltype([](CRITICAL_SECTION* p){LeaveCriticalSection(p);})> pUniqueCs2(&Cs, [](CRITICAL_SECTI  ON* p){LeaveCriticalSection(p);});
 //error C3477: lambda 不能出现在未计算的上下文中
 //unique_ptr<CRITICAL_SECTION> pUniqueCs3(&Cs, Fun1);
 //error C2664: “std::unique_ptr<_Ty>::unique_ptr(_RTL_CRITICAL_SECTION *,const std::default_delete<_Ty> &)”: 不能将参数 2 从“`an  onymous-namespace'::<lambda0>”转换为“const std::default_delete<_Ty> &”