概述
静态内存用来保存局部 static 对象、类 static 数据成员 以及任何定义在函数之外的变量。栈内存用来存储定义在函数内部的非 static 对象。分配在静态或栈内存中的对象由编译器自动创建和销毁。对于栈对象,仅在其定义的程序块运行时才存在;static 对象在使用之前分配,在程序结束时销毁。
除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称作自由空间或堆。程序用堆来存储动态分配的对象——即,那些在程序运行时分配的对象。动态对象的生存周期由程序来控制,即当动态对象不再使用时,我们的代码必须显示的销毁它们。
动态内存与智能指针:
c++中内存时通过一对运算符来完成的:new,在动态内存中为对象分配内存空间并返回一个指向该对象的指针,我们可以选择对象进行初始化;delete,接受一个动态对象的指针,销毁该对象并释放与之关联的内存。
c++11标准库中提供了两种智能指针类型来管理动态内存对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr 允许多个指针指向同一个对象;unique_ptr 则 “独占” 所指向的对象。标准库还定义了一个名为 weak_ptr 的伴随类,它是一个弱引用,指向 shared_ptr 所管理的对象。这三种类型都定义在 memory 头文件中
shared_ptr 类:
智能指针也是模板:
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<string > p1;//p可以指向string对象,没有指向构造函数,所以进行默认初始化,指向一个空的string
shared_ptr<list<string>> p2;//p1可以指向list对象
if(p1 && p1->empty()) *p1 = "hi";//如何通过动态指针得到所指向的对象?
return 0;
}
shared_ptr 和 unique_ptr 都支持的操作:
shared_ptr sp, unique_ptr up | 空智能指针,可以指向类型为 T 的对象 |
---|---|
p | 将 p 用作一个条件判断,若 p 指向一个对象,则为 true |
*p | 解引用 p,获得它指向的对象 |
p->mem | 等价于 (*p).mem |
p.get() | 返回 p 中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。通常用于得到 p 中保存的传统风格指针,因为用shared_ptr直接给普通指针赋值或初始化是非法的 |
p.swap(q) ,swap(p, q) | 交换 p 和 q 中的指针 |
shared_ptr 独有的操作:
make_shared(args) | 返回一个 shared_ptr,指向一个动态分配的类型为 T 的对象。使用 args 初始化此对象 |
---|---|
shared_ptrp(q) | p 是 shared_ptr q 的拷贝:此操作会递增 q 中的计数器。q 中的指针必须能转换为 T* |
p = q | p 和 q 都是 shared_ptr,所保存的指针必须能相互转换。此操作会递减 p 的引用计数,递增 q 的引用计数;若 p 的引用计数变为 0,则将其管理的原内存释放 |
p.unique() | 若 p.use_count() 为 1,返回 true,否则返回 false |
p.use_count() | 返回与 p 共享对象的智能指针数量;可能很慢,主要用于调试 |
make_shared 函数:
最安全的分配和使用动态内存的方法是调用一个名为 make_shared 的标准库函数,我们应该尽量适用
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<int> p1=make_shared<int>(42);
cout<<*p1<<endl;
shared_ptr<string> p2=make_shared<string>(9,'c');
cout<<*p2<<endl;
shared_ptr<int> p3=make_shared<int>();
cout<<*p3<<endl;//默认初始化(值初始化)
}
注意:使用 make_shared 时,必须指定要创建的对象类型。定义方式与模板类相同,在函数名后跟一个尖括号,在其中给出类型
类似顺序容器的 emplace 成员,make_shared 用其参数来构造给定类型的对象。因此给的参数必须符合对应类型的构造规则
用 auto实现以上程序
#include<bits/stdc++.h>
using namespace std;
int main(){
auto p1=make_shared<int>(42);
cout<<*p1<<endl;
auto p2=make_shared<string>(9,'c');
cout<<*p2<<endl;
auto p3=make_shared<int>();
cout<<*p3<<endl;//默认初始化(值初始化)
return 0;
}
拷贝shared_ptr指针
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<int> p=make_shared<int>(26);
shared_ptr<int> q(p);
cout<<q.use_count()<<endl;
cout<<q.use_count()<<endl;
shared_ptr<int> t=q;
cout<<t.use_count()<<endl;
cout<<p.use_count()<<endl;
return 0;
}
我们可以认为每个 shared_ptr 都有一个关联的计数器,通常称其为引用计数。无论何时我们拷贝一个 shared_ptr,计数器都会递增。如,当用一个 shared_ptr 初始化另一个 shared_ptr,或将它作为参数传递给另一个函数以及作为函数的返回值时,它所关联的计数器都会递增。当我们给 shared_ptr 赋予一个新值或是 shared_ptr 被销毁(如,一个局部的 shared_ptr 离开其作用域)时,计数器都会递减。一旦一个 shared_ptr 的计数器变为 0,它就会自动释放自己所管理的对象:
shared_ptr 自动销毁所管理的对象:
当指向对象的最后一个 shared_ptr 被销毁时,shared_ptr 会自动通过析构函数销毁此对象 。由于在最后一个 shared_ptr 销毁之前内存都不会释放,保证 shared_ptr 在无用之后不再保存就十分·重要了。如果忘记销毁程序不再需要的 shared_ptr,程序仍会正确执行,但会浪费内存。shared_ptr 在无用之后仍然保留的的一种可能情况是,你将 shared_ptr 存放在一个容器中,随后重排了容器,从而不再需要某些元素。这种情况下,你应该确保用 erase 删除那些不再需要的 shared_ptr 元素
使用了动态生存期的资源的类:
程序使用动态内存处于以下三种原因之一:
1.程序不知道自己需要使用多少对象
2.程序不知道所需对象的准确类型
3.程序需要在多个对象间共享数据
我们来分析一波第 3 种情况:
我们之前用过的类中,分配的资源都与对应对象生存期一致。如:每个 vector “拥有” 其自己的元素。当我们拷贝一个 vector 时,原 vector 和 副本 vector 中的元素是相互分离的:
但某些类分配的资源具有与原对象相独立的生存期。如,假定我们希望定义一个名为 Blod 的类,保存一组元素。与容器不同,我们希望 Blod 对象的不同拷贝之间共享相同的元素。即,当我们拷贝一个 Blod 时,原 Blod 对象及其拷贝应该引用相同的底层元素。
通常,如果两个对象共享底层数据,当某个对象被销毁时,我们不能单方面销毁底层数据:
定义一个管理 string 的 Blod 类,命名为 strBlod:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class strBlod{
public:
typedef std::vector<std::string>::size_type size_type;
strBlod();
strBlod(std::initializer_list<std::string> il);
~strBlod();
size_type size() const {
return data->size();
}
bool empty() const {
return data->empty();
}
//添加和删除元素
void push_back(const std::string &t){
data->push_back(t);
}
void pop_back();
//元素访问
std::string& front();
std::string& front() const;
std::string& back();
std::string& back() const;
int get_use_count() const{//得到shared_ptr成员data的引用计数
return data.use_count();
}
private:
std::shared_ptr<std::vector<std::string>> data;
//如果data[i]不合法,抛出一个异常
void check(size_type i, const std::string &msg) const;
std::string& front_display() const;
std::string& back_display() const;
};
strBlod::strBlod() : data(make_shared<vector<string>>()) {}//默认构造函数
strBlod::strBlod(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {}
strBlod::~strBlod() {}
void strBlod::check(size_type i, const string &msg) const {
if(i >= data->size()) throw out_of_range(msg);
}
string& strBlod::front(){
return front_display();
}
string& strBlod::front() const{
return front_display();
}
string& strBlod::front_display() const{
//如果vector为空,check会抛出一个异常
check(0, "front on empty strBlod!");
return data->front();
}
string& strBlod::back(){
return back_display();
}
string& strBlod::back() const{
return back_display();
}
string& strBlod::back_display() const{
check(0, "back on empty strBlod!");
return data->back();
}
void strBlod::pop_back(){
check(0, "pop_back on empty strBlod!");
data->pop_back();
}
int main(void){
strBlod b1;//b1执行默认构造
cout << b1.get_use_count() << endl;//1
{
strBlod b2({"hello", "word", "!"});
cout << b2.get_use_count() << endl;//1
b1 = b2;//b1和b2共享元素
cout << b1.get_use_count() << endl;//2
cout << b1.get_use_count() << endl;//2
// 当我们拷贝、赋值或销毁一个 strBlod 对象时,它的 shared_ptr 成员会被拷贝、赋值或销毁
}
// string s = b2.front();//错误,离开了b2的作用域,b2被销毁了
//b1指向原本由b2创建的元素
cout << b1.get_use_count() << endl;//1
string s = b1.front();
cout << s << endl;//hello
return 0;
}//离开b1的作用域,b1被销毁
//由strBlod构造函数分配的vector已经没有strBlod对象指向它,此时被自动销毁
注意:当我们拷贝、赋值或销毁一个 strBlod 对象时,它的 shared_ptr 成员会被拷贝、赋值或销毁
如果一个 shared_ptr 的引用计数变为 0,它所指像的对象会被自动销毁。因此,对于由 strBlod 构造函数分配的 vector,当最后一个指向它的 strBlod 对象被销毁时,它也会随之被自动销毁
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class strBlod{
public:
typedef std::vector<std::string>::size_type size_type;
strBlod();
strBlod(std::initializer_list<std::string> il);
~strBlod();
size_type size() const {
return data->size();
}
bool empty() const {
return data->empty();
}
//添加和删除元素
void push_back(const std::string &t){
data->push_back(t);
}
void pop_back();
//元素访问
std::string& front();
std::string& front() const;
std::string& back();
std::string& back() const;
int get_use_count() const{//得到shared_ptr成员data的引用计数
return data.use_count();
}
private:
std::shared_ptr<std::vector<std::string>> data;
//如果data[i]不合法,抛出一个异常
void check(size_type i, const std::string &msg) const;
std::string& front_display() const;
std::string& back_display() const;
};
strBlod::strBlod() : data(make_shared<vector<string>>()) {}//默认构造函数
strBlod::strBlod(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {}
strBlod::~strBlod() {}
void strBlod::check(size_type i, const string &msg) const {
if(i >= data->size()) throw out_of_range(msg);
}
string& strBlod::front(){
return front_display();
}
string& strBlod::front() const{
return front_display();
}
string& strBlod::front_display() const{
//如果vector为空,check会抛出一个异常
check(0, "front on empty strBlod!");
return data->front();
}
string& strBlod::back(){
return back_display();
}
string& strBlod::back() const{
return back_display();
}
string& strBlod::back_display() const{
check(0, "back on empty strBlod!");
return data->back();
}
void strBlod::pop_back(){
check(0, "pop_back on empty strBlod!");
data->pop_back();
}
int main(void){
strBlod b1;//b1执行默认构造
cout << b1.get_use_count() << endl;//1
{
strBlod b2({"hello", "word", "!"});
cout << b2.get_use_count() << endl;//1
b1 = b2;//b1和b2共享元素
cout << b1.get_use_count() << endl;//2
cout << b1.get_use_count() << endl;//2
// 当我们拷贝、赋值或销毁一个 strBlod 对象时,它的 shared_ptr 成员会被拷贝、赋值或销毁
}
// string s = b2.front();//错误,离开了b2的作用域,b2被销毁了
//b1指向原本由b2创建的元素
cout << b1.get_use_count() << endl;//1
string s = b1.front();
cout << s << endl;//hello
return 0;
}//离开b1的作用域,b1被销毁
//由strBlod构造函数分配的vector已经没有strBlod对象指向它,此时被自动销毁
直接管理内存:
使用 new 动态分配和初始化对象:
#include<bits/stdc++.h>
using namespace std;
int main(){
int *p1=new int;//p1指向一个未初始化的对象,
cout<<*p1<<endl;//默认情况下,内置类型默认分配的内存执行默认初始化
string *s1=new string;//对于类类型执行构造函数初始化
cout<<*s1<<endl;//空的字符串
//在类型后跟一个括号是默认初始化
int *p2=new int(1024);//构造初始化
cout<<*p2<<endl;
string *s2=new string(10,'c');//构造初始化
cout<<*s2<<endl;
//使用auto进行初始化
int obj1=1024;
auto p3=new auto(obj1);
cout<<*p3<<endl;
string obj2("使用auto进行初始化");
auto *s3=new auto(obj2);
cout<<*s3<<endl;
int obj3=1,obj4=2,obj5=3;
// auto *t=new auto{obj3,obj4,obj5};//不能用花括号赋值
//使用const动态分配对象
const int *p4=new const int(1024);
cout<<*p4<<endl;//动态分配一个const对象。必须显式初始化
return 0;
}
注意:在自由空间分配的内存是无名的,因此 new 无法为其分配的对象命名,而是返回一个指向该对象的指针
默认情况下,动态内存分配的对象是默认初始化的,即,内置类型或组合类型的对象的值将是未定义的(随机值),而类类型对象将用默认构造函数进行初始化
对于定义了自己的构造函数的类类型来说,要求值初始化是没有意义的,不管采用什么形式,对象都会通过默认构造函数类初始化
对于内置类型,应该尽量使用值初始化来代替默认初始化,因为其默认初始化是未定义的
如果我们提供了一个括号包围的初始化器,就可以使用 auto 从此初始化器来推断类型。但是由于编译器要用初始化器的类型来推断分配的类型,只有当括号中仅有单一初始化器是才可以使用 auto
动态分配 const 对象时对于没有默认构造函数的类类型,必须显示初始化
内存耗尽:
当一个程序用完了自由空间,那么它的内存就会耗尽,new表达式失败,所以就会抛出bad_alloc异常
我们可以改变使用 new 的方式来阻止它抛出异常:
#include<bits/stdc++.h>
using namespace std;
int main(){
int p1=new int();//
int p1=new (nothrow)int;//这样就不会抛出异常了,但是必须保证这样做程序是正确的
return 0;
}
释放动态内存:
#include<bits/stdc++.h>
using namespace std;
int main(){
//delete指向一个指针,指针指向对象,这个指针必须指向对象或者是一个空指针。
int i,*pi=&i;
// delete i;//不是一个指针。报错
delete pi;//编译通过,但是指针指向一个局部变量,行为未定义
int *p=new int(1024),*p1=p;
delete p;//释放动态内存
cout<<"***"<<endl;
// delete p1;//多次释放行为未定义。
const int *p2=new const int(11);
delete p2;//正确释放一个const动态内存
return 0;
}
动态内存管理常见错误:
1.忘记 delete 内存,导致内存泄漏
2.使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误
3.同一块内存释放多次
delete 之后重置指针:
在 delete 之后,指针就变成了空悬指针。可以在 delete 之后将 nullptr 赋予指针,这样就清楚地指出指针不指向任何对象。但是,动态内存可能有多个指针指向相同的内存。在 delete 之后重置指针的方法只对这个指针有效,对其它任何仍指向(已释放的)内存的指针是没有作用的:
#include<bits/stdc++.h>
using namespace std;
int main(){
//delete指向一个指针,指针指向对象,这个指针必须指向对象或者是一个空指针。
int i,*pi=&i;
// delete i;//不是一个指针。报错
delete pi;//编译通过,但是指针指向一个局部变量,行为未定义
pi=nullptr;
int *p=new int(1024),*p1=p;
delete p;//释放动态内存
cout<<"***"<<endl;
// delete p1;//多次释放行为未定义。
const int *p2=new const int(11);
delete p2;//正确释放一个const动态内存
p2=nullptr;
return 0;
}
shared_ptr 和 new 结合使用:
#include<bits/stdc++.h>
using namespace std;
shared_ptr<int> clone(int p){
return new int(p);//因为是explicit的构造函数,所以创建临时变量的时候遇到了错误。
return shared_ptr<int>(new int (p));//正确,显式转换
}
int main(){
//不初始化智能指针,智能指针就被初始化为空指针
//但是可以使用内置指针初始化智能指针
shared_ptr<double> p1;
shared_ptr<double> p2(new int(1024));
// shared_ptr<double> p3=new int(1024);//拷贝构造参数是explicit的,不能隐式转换一个内置指针为智能指针
return 0;
}
注意:接受指针参数的智能指针构造函数是 explicit 的,我们不能将一个内置指针隐式转化一个智能指针,必须使用直接初始化形式来初始化一个智能指针
智能指针默认使用 delete 释放它所关联的对象,默认情况下一个用来初始化智能指针的普通指针必须指向动态内存。
我们也可以将智能指针绑定到一个指向其它类型的资源的指针上,但是为了这么做,必须提供自己的操作来替代 delete
定义和改变 shared_ptr 的其它方法:
shared_ptr p(q) | p 管理内置指针 q 所指向的对象:q 必须指向 new 分配的内存且能够转换为 T* 类型 |
---|---|
shared_ptr p(u) | p 从 unique_ptr u 那里接管了对象的所有权:将 u 置为空 |
shared_ptr p(q, d) | p 接管了内置指针 q 所指向的对象的所有权。q 必须能转为 T* 类型。p 将使用可调用对象 d 来代替 delete |
shared_ptr p(p2, d) | p 是 shared_ptr p2 的拷贝,且用可调用对象 d 来替代 delete |
p.reset() p.reset(q) p.reset(q, d) | 若 p 是唯一指向其对象的 shared_ptr,reset 会释放此对象。若传递了可选参数指针 q,会令 p 指向 q,否则会将 p 置为空。若还传递了可选参数d,将会调用 d 而不是 delete 来释放 q |
不要混合使用普通指针和智能指针:
#include<bits/stdc++.h>
using namespace std;
void process(shared_ptr<int> ptr){}//离开作用域,ptr计数器减一
int main(){//不要混合使用隐式指针和智能指针
shared_ptr<int> p1(new int(42));
process(p1);//在传参的时候,计数器加一,在退出的时候,计数器减一,对象仍旧存在
cout<<*p1;
int i=9,*x=&i;
process(shared_ptr<int>(x));//错误,此时x已经被释放,成为一个空悬指针
cout<<i<<endl;
return 0;
}
注意:当将一个 shared_ptr 绑定到一个普通指针时,我们就将内存的管理交给了这个 shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问 shared_ptr 所指向的内存了
显示 / 隐式转换时即创建了一个对应的临时变量
离开创建临时变量的表达式时,该表达式中创建的临时变量会被销毁.
不要使用 get 初始化另一个智能指针或为智能指针赋值:
注意:get 通常用于将 shared_ptr 中保存的值赋给普通指针,因为用 shared_ptr 直接给普通指针赋值或初始化是非法的,反之亦然
只有在确定代码不会 delete 指针的情况下才能用 get
永远不要用 get 初始化另一个智能指针或为另一个智能指针赋值
两个相互独立但指向相同的内存的 shared_ptr 各自的引用计数都是 1,着意味着当其中一个自动销毁但另一个没被销毁时,没被销毁的 shared_ptr 会成为空悬指针
使用get返回一个普通指针,不能使用这个普通指针再给一个智能指针赋值
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<int> p(new int(42));
int *q=p.get();
cout<<q<<endl;
{
shared_ptr<int> p1(q);
}//当这个新的作用域结束之后,q指向的内存会被释放,
cout<<*p<<endl;//此时p是一个空悬指针。
return 0;
}
reset()操作
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<int> p=make_shared<int>(1024);
//p=new int(0);错误,因为explicit的构造函数
p.reset(new int (0));
(*p)++;
cout<<*p<<endl;//将p指向了0的动态内存对象
if(!p.unique()) p.reset(new int (*p));
*p+=1;
cout<<*p<<endl;
return 0;
}
智能指针和异常:
void f(){
int ip=new int(5);
/**///这里发生了异常,导致程序结束
delete ip;//那些内存永远也不会被释放了。但是如果ip是一个智能指针,不论异常还是作用域结束,内存都会被释放
}
注意:使用内置指针管理内存,且在 new 之后再对应的 delete 之前发生了异常,则内存不会被释放。但如果上例中 ip 是一个 shared_ptr,则仍会在其作用域结束时正常释放
删除器:
当一个 shared_ptr 管理的对象被释放时,默认是进行 delete 操作,而 delete 操作是调用对应对象的析构函数来完成的。这意味这对于分配了资源而又没有定义析构函数的类,我们需要定义一个函数来代替 delete 完成对 shared_ptr 中保存的指针进行释放操作,即删除器:
#include <iostream>
using namespace std;
struct destination;//表示我们正在连接什么
struct connection;//使用连接所需的信息
connection connect(destination*);//打开连接
void disconnect(connection);//关闭给定的连接
void end_connection(connection *p){//删除器
disconnect(*p);
}
void f(destination &d /*其它参数*/){
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
//使用连接
//当f推出时,connection会被正确关闭
}
注意:当 p 被销毁时,它不会对自己保存的指针执行 delete,而是直接调用 end_connection
智能指针的陷阱:
智能指针的安全性是建立在正确使用的前提下的,我们必须坚持一些基本规范:
不使用相同的内置指针初始化(或 reset)多个智能指针。这会造成前面我们提到的相互独立的 shared_ptr 之间共享相同地址的问题
不 delete get() 返回的指针
不适用 get() 初始化或 reset 另一个智能指针
如果使用 get() 返回的指针,记住当最后一个对应的 shared_ptr 销毁后,之前 get() 返回的的指针就无效了
如果使用智能指针管理的不是 new 分配的内存,必须传给它一个删除器
unique_ptr:
与 shared_ptr 不同,某个时刻只能有一个 unique_ptr 指向给定的对象。当 unique 被销毁时,它所指向的对象也被销毁
unique_ptr 操作:
unique_ptr u1 | 空 unique_ptr,可以指向一个类型为 T 的对象。u1 会使用 delete 来释放它的 |
---|---|
unique_ptr<T, D> u2 | 指针;u2 会使用一个类型为 D 的可调用对象来释放它的指针,初始化 u2 时除了需要一个能转化成 T* 类型的对象外还需要一个 D 类型的对象来代替delete |
unique_ptr<T, D> u(d) | 空 unique_ptr,指向类型为 T 的对象,用类型为 D 的对象 d 代替 delete |
u = nullptr | 释放 u 指向的对象,将 u 置为空 |
u.release() | u 放弃对指针的控制权,返回指针,并将 u 置为空 |
u.reset() | 释放 u 指向的对象 |
u.reset(q) | 如果提供了内置指针 q,令 u 指向这个对象,否则将 u 置为空u.reset(nullptr) |
初始化 unique_ptr 必须采用直接初始化形式:
unique_ptr p1;//可以指向一个double的unique_ptr
unique_ptr p2(new int(42));//p2指向一个值为42的int
#include<bits/stdc++.h>
using namespace std;
int main(){
unique_ptr<string> p1(new string("string"));
// unique_ptr<string> p2=p1;//错误,不能使用这个赋值
unique_ptr<string> p2;
p2=p1;//赋值运算符函数是=delete的,所以不能使用
return 0;
}
```
unique_ptr 是通过 release 或 reset 将指针的所有权从一个 (非const) unique_ptr 转移给另一个 unique_ptr 的:
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
unique_ptr<string> p1(new string("abc"));//必须采取直接初始化的方式
unique_ptr<string> p2(p1.release());//可以通过release来转移控制权.release放弃控制权,然后将p1置空。
unique_ptr<string> p3(p1.release());//报错,只能采取直接初始化的方式
p1.reset(p2.release());
cout<<*p1<<endl;
return 0;
}
注意:如果 unique_ptr 不为空,则调用 reset 会释放其原来所指的内存。而 release 则不会释放 unique_ptr 原来所指的内存。调用 release 会切断 unique_ptr 和它原来所指对象间的联系,如果我们不是用另一个智能指针来保存 release 返回的指针,则我们的程序就要负责资源的释放:
p2.release();//错误,p2 不会释放内存,而且我们丢失了指针
auto p = p2.release();//正确,但我们必须记得 delete§
传递 unique_ptr 参数和返回 unique_ptr:
不能拷贝 unique_ptr 的规则有一个例外:我们可以拷贝或赋值一个将要销毁的 unique_ptr:
从函数返回一个 unique_ptr:
#include<bits/stdc++.h>
using namespace std;
unique_ptr<int> f(int p){
return unique_ptr<int>(new int(p));
}
unique_ptr<int> f1(int p){
unique_ptr<int>ret(new int(p));
return ret;
}
注意:这两段代码,编译器都知道要返回的对象将要被销毁。在此种情况下,编译器执行一种特殊的 “拷贝” (移动构造函数)
向 unique_ptr 传递删除器:
类似 shared_ptr,unique_ptr 默认情况下用 delete 释放它指向的对象。同样的,我们可以重载一个 unique_ptr 中默认的删除器:
unique_ptr<objT, delT> p(new objT, fcn);
//p 指向一个类型为 objT 的对象,并使用一个类型为 delT 的对象 fnc 来释放 objT 对象
类似于关联容器中重载比较运算函数,等于判断函数,哈希函数等
void f(destination &d /*其它需要的参数*/){
connection c = connect(&d);//打开连接
//当p被销毁时,连接会自动关闭
unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);
//decltype只是返回了一个函数类型,必须添加*来指出我们正在使用一个函数指针
//使用连接
//当f退出时(即使是由于异常而退出),connection会被正常关闭
}
weak_ptr:
weak_ptr 指向一个 shared_ptr 管理的对象。将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数。一旦最后一个指向对象的 shared_ptr 被销毁,即便有 weak_ptr 指向对象,对象也还是会被释放的
weak_ptr 的操作:
weak_ptr w | 空 weak_ptr 可以指向类型为 T 的对象 |
---|---|
weak_ptr w(sp) | 与 shared_str sp 指向相同对象的 weak_ptr。T 必须能转化为 sp 指向的对象的类型 |
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 |
w.lock() | 如果 w.expired() 为 true,返回一个空 shared_ptr,否则返回一个指向 w 的对象的 shared_ptr |
创建一个 weak_ptr 时,要用一个 shared_ptr 来初始化它:
#include<bits/stdc++.h>
using namespace std;
int main(){
//weak_ptr不控制所指对象生存周期的函数、
auto p=make_shared<int>(42);
weak_ptr<int> p1(p);//创建一个 weak_ptr 时,要用一个 shared_ptr 来初始化它:,而不能用其他的类型来初始化
cout<<p1.use_count()<<endl;;//返回有多少个shared指指针指向这个对象
weak_ptr<int> p2=p,p3=p1;//赋值初始化
int *cnt=new int(1024);
// weak_ptr<int> b=cnt;//错误,只能shared_ptr赋值
// weak_ptr<int> b(cnt);
return 0;
}
使用 weak_ptr 时,由于我们不确定其所指对象是否存在,要先调用 lock 函数检查一下其所指对象是否存在
#include<bits/stdc++.h>
using namespace std;
int main(){
shared_ptr<int> q(new int(45));
weak_ptr<int> p(q);
cout<<p.lock()<<endl;
if(shared_ptr<int> t=p.lock()){//如果nq不为空,则条件成立
cout<<"yes"<<endl;
}
}
最后
以上就是怕孤独大炮为你收集整理的C++动态内存与智能指针的全部内容,希望文章能够帮你解决C++动态内存与智能指针所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复