新手如何学网站建设,做公司网站需要准备什么科目,河南郑州最新消息,石家庄php网站建设前言普通指针的不足new和new门的内存需要用delete和delete[释放。程序员的主观失误#xff0c;忘了或漏了释放程序员也不确定何时释放#xff08;例如多个线程共享同一个对象#xff0c;没办法确定什么时候释放#xff09;普通指针的释放类内的指针#xff0c;在析构函数中…前言普通指针的不足new和new门的内存需要用delete和delete[释放。程序员的主观失误忘了或漏了释放程序员也不确定何时释放例如多个线程共享同一个对象没办法确定什么时候释放普通指针的释放类内的指针在析构函数中释放C内置数据类型如何释放?new出来的类本身如何释放?智能指针设计思路智能指针是类模板在栈上创建智能指针对象把普通指针交给智能指针对象智能指针对象过期时调用析构函数释放普通指针的内存什么是智能指针智能指针是RALL机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针本质上却是一个对象这样可以方便管理一个对象的生命周期。智能指针详解unique_ptr定义unique_ptr独享它指向的对象也就是说同时只有一个unique_ptr指向同一个对象当这个unique_ptr被销毁时指向的对象也随即被销毁。头文件#includememorytemplate typename T, typename D default_deleteT
class unique_ptr
{
public:explicit unique_ptr(pointer p) noexcept; // 不可用于转换函数。~unique_ptr() noexcept; T operator*() const; // 重载*操作符。T* operator-() const noexcept; // 重载-操作符。unique_ptr(const unique_ptr ) delete; // 禁用拷贝构造函数。unique_ptr operator(const unique_ptr ) delete; // 禁用赋值函数。unique_ptr(unique_ptr ) noexcept; // 右值引用。unique_ptr operator(unique_ptr ) noexcept; // 右值引用。// ...
private:pointer ptr; // 内置的指针。
};下面进行测试#includememory
#includeiostream
#includevector
using namespace std;
class AA
{
public:string m_name;AA() { cout m_name 调用构造函数AA()。 endl;}AA(const string name) : m_name(name) {cout 调用构造函数AA( m_name )。 endl;}~AA() { cout 调用了析构函数~AA( m_name )。 endl;}
};测试1void test1()
{AA* p new AA(张三);//delete p; //这里结社我们忘记delete
}运行结果调用构造函数AA(张三)。我们发现出现了内存泄漏问题这里我们是故意忘记释放但是真正到了开发时都有可能会忘记释放那怎么办呢我们可以用智能指针unique_ptr测试2void test2()
{AA* p new AA(张三);unique_ptrAA p1(p);
}运行结果调用构造函数AA(张三)。张三调用了析构函数~AA(张三)。我们惊奇的发现我们没有调用delete但是也调用了析构函数因为智能指针是一个类它有析构函数在析构函数中有delete语句。并且类重载了-和*可以像使用普通指针一样使用智能指针测试3void test3()
{AA* p new AA(张三);unique_ptrAA p1(p);cout (*p1).m_name endl;cout p1-m_name endl;cout p-m_name endl;cout (*p).m_name endl;
}运行结果调用构造函数AA(张三)。张三张三张三张三张三调用了析构函数~AA(张三)。基本用法初始化//方法1
unique_ptrAA p0(new AA(西施)); // 分配内存并初始化
//方法2
unique_ptrAA p0 make_uniqueAA(西施); // C14标准。
unique_ptrint pp1make_uniqueint(); // 数据类型为int。
unique_ptrAA pp2 make_uniqueAA(); // 数据类型为AA默认构造函数。
unique_ptrAA pp3 make_uniqueAA(西施); // 数据类型为AA一个参数的构造函数。
unique_ptrAA pp4 make_uniqueAA(西施,8); // 数据类型为AA两个参数的构造函数。
//方法3
AA* p new AA(西施);
unique_ptrAA p0(p); // 用已存在的地址初始化。注意重载了-和*可以像使用普通指针一样使用unique_ptr不支持普通的拷贝和赋值explicit unique_ptr(pointer p) noexcept; // 因为有explicit所以不可用于转换函数。unique_ptr(const unique_ptr ) delete; // 禁用拷贝构造函数unique_ptr operator(const unique_ptr ) delete; // 禁用赋值函数。例如以下测试错误的void test4()
{AA* p new AA(张三);unique_ptrAA p1 p; //错误不能将普通指针直接复制给智能指针unique_ptrAAp2 new AA(李四); //错误不能将普通指针直接复制给智能指针unique_ptrAA p3 pu1; // 错误不能用其它unique_ptr拷贝构造unique_ptrAA p4;p4p1; //错误不能用对unique_ptr进行赋值
}为什么要禁用赋值函数呢这个与智能指针实现初衷有关unique_ptr实现的是一个指针独享一份资源如果可以复制的话那个就会出现多个unique_ptr指针指向同一块资源就会出现非法操作野指针。不要用同一个裸指针初始化多个unique_ptr对象也是出现非法操作野指针问题不要用unique_ptr管理不是new分配的内存get()方法返回裸指针用于函数的参数只能传引用不支持指针运算、-、、--。技巧1. 将一个unique_ptr赋给另一个时如果原unique_ptr是一个临时右值编译器允许这样做如果源unique_ptr将存在一段时间编译器禁止这样做。一般用于函数的返回值2. 用nullptr给unique_ptr赋值将释放对象空的unique_ptrnullptr3. release()释放对原始指针的控制权将unique_ptr置为空返回裸指针。可用于把unique_ptr传递给子函数子函数将负责释放对象4. std::move()可以转移对原始指针的控制权。可用于把unique_ptr传递给子函数子函数形参也是unique_ptr5. reset()释放对象6. swap()交换两个unique_ptr的控制权7. unique_ptr也可象普通指针那样当指向一个类继承体系的基类对象时也具有多态性质如同使用裸指针管理基类对象和派生类对象那样8. unique_ptr不是绝对安全如果程序中调用exit()退出全局的unique_ptr可以自动释放但局部的unique_ptr无法释放9. unique_ptr提供了支持数组的具体化版本。数组版本的unique_ptr重载了操作符[]操作符[]返回的是引用可以作为左值使用。这里只演示第1、4、5、8、9示例 1unique_ptrAA func()
{unique_ptrAA pp(new AA(小谢));return pp;
}
void test4()
{unique_ptrAA p1(new AA(张三));unique_ptrAA p2;//p2 p1;p2 unique_ptrAA(new AA(李四));cout 调用func之前 endl;p2 func();cout 调用func之后 endl;
}示例 4void func(unique_ptrAA a)
{cout a-m_name endl;
}
void test5()
{unique_ptrAApu(new AA(张三));cout 开始调用函数 endl;func(move(pu));cout 调用函数结束 endl;
}这里函数调用时使用了move函数将pu的管理权交给形参示例 5void reset(T * _ptr (T *) nullptr);pp.reset(); // 释放pp对象指向的资源对象。pp.reset(nullptr); // 释放pp对象指向的资源对象pp.reset(new AA(bbb)); // 释放pp指向的资源对象同时指向新的对象。示例 8unique_ptrAA p(new AA(全局));
int main()
{unique_ptrAA p1(new AA(局部));exit(0);
}结果调用构造函数AA(全局)。调用构造函数AA(局部)。全局调用了析构函数~AA(全局)。示例 9unique_ptrint[] parr1(new int[3]); // 不指定初始值。
unique_ptrint[] parr1(new int[3]{ 33,22,11 }); // 指定初始值。
cout parr1[0] parr1[0] endl;
cout parr1[1] parr1[1] endl;
cout parr1[2] parr1[2] endl;shared_ptr定义shared_ptr共享它指向的对象多个shared_ptr可以指向关联相同的对象在内部采用计数机制来实现。当新的shared_ptr与对象关联时引用计数增加1。当shared_ptr超出作用域时引用计数减1。当引用计数变为0时则表示没有任何shared_ptr与对象关联则释放该对象。基本用法初始化shared_ptr的构造函数也是explicit但是没有删除拷贝构造函数和赋值函数。//方法一
shared_ptrAA p0(new AA(西施)); // 分配内存并初始化。
//方法二
shared_ptrAA p0 make_sharedAA(西施); // C11标准效率更高。
shared_ptrint pp1make_sharedint(); // 数据类型为int。
shared_ptrAA pp2 make_sharedAA(); // 数据类型为AA默认构造函数。
shared_ptrAA pp3 make_sharedAA(西施); // 数据类型为AA一个参数的构造函数。
shared_ptrAA pp4 make_sharedAA(西施,8); // 数据类型为AA两个参数的构造函数。
//方法三
AA* p new AA(西施);
shared_ptrAA p0(p); // 用已存在的地址初始化。
//方法四
shared_ptrAA p0(new AA(西施));
shared_ptrAA p1(p0); // 用已存在的shared_ptr初始化计数加1。
shared_ptrAA p1p0; // 用已存在的shared_ptr初始化计数加1注意智能指针重载了*和-操作符可以像使用指针一样使用shared_ptruse_count()方法返回引用计数器的值unique()方法如果use_count()为1返回true否则返回falseshared_ptr支持赋值左值的shared_ptr的计数器将减1右值shared_ptr的计算器将加1当某个资源没有指向时就会被释放get()方法返回裸指针不要用同一个裸指针初始化多个shared_ptr不要用shared_ptr管理不是new分配的内存用于函数的参数只能传引用不支持指针运算、-、、--。技巧1. 用nullptr给shared_ptr赋值将把计数减1如果计数为0将释放对象空shared_ptrnullptr2. std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr3. reset()改变与资源的关联关系pp.reset(); // 解除与资源的关系资源的引用计数减1。pp. reset(new AA(bbb)); // 解除与资源的关系资源的引用计数减1。关联新资源。4. swap()交换两个shared_ptr的控制权void swap(shared_ptrT _Right);5. shared_ptr也可象普通指针那样当指向一个类继承体系的基类对象时也具有多态性质如同使用裸指针管理基类对象和派生类对象那样6. shared_ptr不是绝对安全如果程序中调用exit()退出全局的shared_ptr可以自动释放但局部的shared_ptr无法释放7. shared_ptr提供了支持数组的具体化版本数组版本的shared_ptr重载了操作符[]操作符[]返回的是引用可以作为左值使用8. shared_ptr的线程安全性shared_ptr的引用计数本身是线程安全引用计数是原子操作。多个线程同时读同一个shared_ptr对象是线程安全的。如果是多个线程对同一个shared_ptr对象进行读和写则需要加锁。多线程读写shared_ptr所指向的同一个对象不管是相同的shared_ptr对象还是不同的shared_ptr对象也需要加锁保护。9. 如果unique_ptr能解决问题就不要用shared_ptr。unique_ptr的效率更高占用的资源更少。weak_ptr定义weak_ptr 是为了配合shared_ptr而引入的它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向如果最后一个指向资源的shared_ptr被销毁资源就会被释放。weak_ptr更像是shared_ptr的助手而不是智能指针这里举个例子#include iostream
#include memory
using namespace std;
class BB;
class AA
{
public:string m_name;AA() { cout m_name 调用构造函数AA()。\n; }AA(const string name) : m_name(name) { cout 调用构造函数AA( m_name )。\n; }~AA() { cout 调用了析构函数~AA( m_name )。\n; }shared_ptrBB m_p;
};
class BB
{
public:string m_name;BB() { cout m_name 调用构造函数BB()。\n; }BB(const string name) : m_name(name) { cout 调用构造函数BB( m_name )。\n; }~BB() { cout 调用了析构函数~BB( m_name )。\n; }shared_ptrAA m_p;
};
int main()
{shared_ptrAA pa make_sharedAA(西施a);shared_ptrBB pb make_sharedBB(西施b);pa-m_p pb;pb-m_p pa;
}程序结果显然AA和BB都没有析构这是为什么呢AA中有类BB的指针指向pbBB中有类AA的指针指向pa所以pa和pb不知道谁先“死”所以程序没办法判断谁先析构最后干脆不析构了。那怎么解决这个问题呢智能指针的循环引用问题这里引出了weak_ptr指针代码修改如下基本用法weak_ptr没有重载 -和 *操作符不能直接访问资源。有以下成员函数1operator(); // 把shared_ptr或weak_ptr赋值给weak_ptr。2expired(); // 判断它指资源是否已过期已经被销毁。3lock(); // 返回shared_ptr如果资源已过期返回空的shared_ptr。4reset(); // 将当前weak_ptr指针置为空。5swap(); // 交换。weak_ptr不控制对象的生命周期但是它知道对象是否还活着。用lock()函数把它可以提升为shared_ptr如果对象还活着返回有效的shared_ptr如果对象已经死了提升会失败返回一个空的shared_ptr。提升的行为lock()是线程安全的。进行测试int main()
{shared_ptrAA pa make_sharedAA(西施a);{shared_ptrBB pb make_sharedBB(西施b);pa-m_p pb;pb-m_p pa;shared_ptrBB pp pa-m_p.lock(); // 把weak_ptr提升为shared_ptr。if (pp nullptr)cout 语句块内部pa-m_p已过期。\n;elsecout 语句块内部pp-m_name pp-m_name endl;}shared_ptrBB pp pa-m_p.lock(); // 把weak_ptr提升为shared_ptr。if (pp nullptr)cout 语句块外部pa-m_p已过期。\n;elsecout 语句块外部pp-m_name pp-m_name endl;
}运行结果删除器#include iostream
#include memory
using namespace std;class AA
{
public:string m_name;AA() { cout m_name 调用构造函数AA()。 endl; }AA(const string name) : m_name(name) { cout 调用构造函数AA( m_name )。 endl; }~AA() { cout 调用了析构函数~AA( m_name )。 endl; }
};void deletefunc(AA* a) { // 删除器普通函数。cout 自定义删除器全局函数。 endl;delete a;
}struct deleteclass // 删除器仿函数。
{void operator()(AA* a) {cout 自定义删除器仿函数。 endl;delete a;}
};auto deleterlamb [](AA* a) { // 删除器Lambda表达式。cout 自定义删除器Lambda。 endl;delete a;
};int main()
{shared_ptrAA pa1(new AA(张三)); //不填默认使用缺省//shared_ptrAA pa1(new AA(张三), deletefunc);//shared_ptrAA pa2(new AA(李四), deleteclass());//shared_ptrAA pa3(new AA(王五), deleterlamb);//unique_ptrAA,decltype(deletefunc)* pu1(new AA(张三), deletefunc);//unique_ptrAA, void (*)(AA*) pu0(new AA(李四), deletefunc);//unique_ptrAA, deleteclass pu2(new AA(王五), deleteclass());//unique_ptrAA, decltype(deleterlamb) pu3(new AA(小谢), deleterlamb);return 0;
}