赞
踩
目录
C++指针包含两种
智能指针和原始指针的关系
并不是所有的指针可以封装为智能指针,很多时候原始指针更方便
各种指针中,最常用的是原始指针,其次是unique_ptr和shared_ptr
weak_ptr是share_ptr的一种补充,应用场景少
接下来思考一个问题,为什么有了智能指针,还需要rust呢?
特点:
三种创建方式:
unique_ptr可以通过get()获取地址,另外可以通过->调用成员函数,通过*进行解引用。
- //cat.h
- #ifndef CAT_H
- #define CAT_H
- #include <string>
- #include <iostream>
- #include "cat.h"
- class Cat
- {
- public:
- Cat(std::string name);
- Cat() = default;
- ~Cat();
- void cat_info() const
- {
- std::cout << "cat info name: " << name << std::endl;
- }
- std::string get_name() const
- {
- return name;
- }
- void set_cat_name(const std::string& name)
- {
- this->name = name;
- }
- private:
- std::string name{ "Mini" };
- };
- #endif

- //cat.cpp
- #include "cat.h"
-
- Cat::Cat(std::string name) : name(name)
- {
- std::cout << "Constructor of Cat :" << name << std::endl;
- }
- Cat::~Cat()
- {
- std::cout << "Destructor of Cat :" << name << std::endl;
- }
- //main.cpp
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- int main(int argc, char *argv[])
- {
- Cat c1("OK");
- c1.cat_info();
- {
- Cat c1("OK");
- c1.cat_info();
- }
- cout << "-------------yz----------" << endl;
- return 0;
- }

- //没有delete由new创建的指针
- #include<iostream>
- #include<memory>
- #inlclude"cat.h"
- using namespace std;
-
- int main()
- {
- Cat *cp1 = new Cat("");
- cp1->cat_info();
- {
- Cat *cp1 = new Cat("yy");
- cp1->cat_info();
- }
- cout<< "-------------yz----------";
- return 0;
- }

以上代码是有问题的,我们没有去释放申请的内存,是不会调用析构函数,内存得不到释放。另外当我们delete两次指针的时候也会出现错误。
因此引入unique_ptr智能指针解决上述问题
代码实现如下
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- int main(int argc, char *argv[])
- {
- //创建unique_ptr方式1
- Cat *cp1 = new Cat("yz");
- std::unique_ptr<Cat> u_cp1(cp1);
- u_cp1->cat_info();
- delete cp1;
- cp1 = nullptr;
-
- cout << "-------------yz----------" << endl;
-
- //创建unique_ptr方式2
- std::unique_ptr<Cat> u_cp2(new Cat("gg"));
- u_cp2->cat_info();
- cout << "-------------yz----------" << endl;
-
- //创建unique_ptr方式3
- std::unique_ptr<Cat> u_cp3 = make_unique<Cat>();
- u_cp3->cat_info();
- return 0;
-
- }

但是需要注意的是,方式一好像在最新的g++编译器上不需要delete和置空操作。如下为我的上述代码的输出。
- Constructor of Cat :yz
- cat info name: yz
- Destructor of Cat :yz
- -------------yz----------
- Constructor of Cat :gg
- cat info name: gg
- -------------yz----------
- cat info name: Mini
- Destructor of Cat :Mini
- Destructor of Cat :gg
- Destructor of Cat :free(): double free detected in tcache 2
- Aborted (core dumped)
如果我将delete操作和置空操作删除,例如
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- int main(int argc, char *argv[])
- {
- //创建unique_ptr方式1
- Cat *cp1 = new Cat("yz");
- std::unique_ptr<Cat> u_cp1(cp1);
- u_cp1->cat_info();
-
- cout << "-------------yz----------" << endl;
-
- //创建unique_ptr方式2
- std::unique_ptr<Cat> u_cp2(new Cat("gg"));
- u_cp2->cat_info();
- cout << "-------------yz----------" << endl;
-
- //创建unique_ptr方式3
- std::unique_ptr<Cat> u_cp3 = make_unique<Cat>();
- u_cp3->cat_info();
- return 0;
-
- }

输出为
- Constructor of Cat :yz
- cat info name: yz
- -------------yz----------
- Constructor of Cat :gg
- cat info name: gg
- -------------yz----------
- cat info name: Mini
- Destructor of Cat :Mini
- Destructor of Cat :gg
- Destructor of Cat :yz
下面介绍解引用和取地址操作,以方式为例
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- int main(int argc, char *argv[])
- {
- //创建unique_ptr方式3
- std::unique_ptr<int> u_cp3 = make_unique<int>(200);
- cout<<"* u_cp3 = "<<* u_cp3<<endl;
- cout<<"get adress"<<u_cp3.get()<<endl;
- return 0;
-
- }
输出为
- * u_cp3 = 200
- get adress0x55e978458e70
注意事项:
Passing by value
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- void Passing_by_value( std::unique_ptr<Cat> c){
- c->cat_info();
- }
-
- int main(int argc, char *argv[])
- {
- //创建unique_ptr方式3
- std::unique_ptr<Cat> u_cp1 = make_unique<Cat>("haha");
- Passing_by_value(std::move(u_cp1));//需要注意std::move来转移内存拥有权,否则报错。另外move后也不能再使用u_cp1
- Passing_by_value(std::make_unique<Cat>("xixi"));
-
- return 0;
-
- }

从下面输出结果看到,在move操作转移所有权后会调用Passing_by_value函数和析构函数,说明智能指针不再可用。
- Constructor of Cat :haha
- cat info name: haha
- Destructor of Cat :haha
- Constructor of Cat :xixi
- cat info name: xixi
- Destructor of Cat :xixi
Passing by reference
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- void Passing_by_ref( const std::unique_ptr<Cat> &c){
- c->set_cat_name("ee");
- c->cat_info();
- }
-
- int main(int argc, char *argv[])
- {
- //创建unique_ptr方式3
- std::unique_ptr<Cat> u_cp1 = make_unique<Cat>("haha");
- Passing_by_ref(u_cp1);//需要注意std::move来转移内存拥有权,否则报错。另外move后也不能再使用u_cp1
- u_cp1->cat_info();
-
- return 0;
-
- }

由下列输出结果可得,传引用可以修改变量值。
- Constructor of Cat :haha
- cat info name: ee
- cat info name: ee
- Destructor of Cat :ee
Return by value
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- std::unique_ptr<Cat> get_unique_ptr()//return by value
- {
- std::unique_ptr<Cat> p_dog = std::make_unique<Cat>("dog");
- cout<<"unique_ptr adresss"<<p_dog.get()<<endl;
- cout<<"unique_ptr adresss"<<&p_dog<<endl;
- return p_dog;
- }
-
- int main(int argc, char *argv[])
- {
-
- get_unique_ptr()->cat_info();//链式调用
-
- return 0;
-
- }

输出为
- Constructor of Cat :dog
- unique_ptr adresss0x55a6558e1e70
- unique_ptr adresss0x7ffeb6c9ae70
- cat info name: dog
- Destructor of Cat :dog
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
-
- int main(int argc, char *argv[])
- {
-
- std::shared_ptr<Cat> p1 = make_shared<Cat>("aa");
- p1->cat_info();
- cout<<"p1 count: "<<p1.use_count()<<endl;
- std::shared_ptr<Cat> p2 = p1;
- cout<<"p1 count: "<<p1.use_count()<<endl;
- cout<<"p2 count: "<<p2.use_count()<<endl;
- p1=nullptr;
- cout<<"p1 count: "<<p1.use_count()<<endl;
- cout<<"p2 count: "<<p2.use_count()<<endl;
- return 0;
-
- }

输出为
- Constructor of Cat :aa
- cat info name: aa
- p1 count: 1
- p1 count: 2
- p2 count: 2
- p1 count: 0
- p2 count: 1
- Destructor of Cat :aa
值得注意的是,无论引用计数为多少,数据只有一套。无论引用计数为多少,程序运行结束时只会调用一次析构函数。
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- void pass_by_value(std::shared_ptr<Cat> c){
- c->cat_info();
- cout<<"count: "<<c.use_count()<<endl;
- }
-
- int main(int argc, char *argv[])
- {
-
- std::shared_ptr<Cat> p1 = make_shared<Cat>("aa");
- p1->cat_info();
- pass_by_value(p1);
- cout<<"p1 count: "<<p1.use_count()<<endl;
-
- return 0;
-
- }

由下面可得,当进入函数体时引用计数加1,离开函数体减1。
- Constructor of Cat :aa
- cat info name: aa
- cat info name: aa
- count: 2
- p1 count: 1
- Destructor of Cat :aa
类似与unique_ptr
l类似与unique_ptr
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
- std::unique_ptr<Cat> pass_by_value(){
- std::unique_ptr<Cat> c = make_unique<Cat>("cc");
- return c;
- }
-
- int main(int argc, char *argv[])
- {
- std::unique_ptr<Cat> p0 = make_unique<Cat>("bb");
- std::shared_ptr<Cat> p1 = std::move(p0);//unique_ptr转换为shared_ptr
- p1->cat_info();
- cout<<"p1 count: "<<p1.use_count()<<endl;
-
-
- std::shared_ptr<Cat> p3 = pass_by_value();//直接用shared_ptr接收unique_ptr
- cout<<"p3 count"<<p3.use_count()<<endl;
- return 0;
-
- }

输出为
- Constructor of Cat :bb
- cat info name: bb
- p1 count: 1
- Constructor of Cat :cc
- p3 count1
- Destructor of Cat :cc
- Destructor of Cat :bb
weak_ptr为什么存在呢?
A类中有一个需求需要存储其他A类对象的信息,如果使用shared_ptr,那么在销毁时会遇到循环依赖问题,所以我们这里需要用一个不需要拥有权的指针来标记同类对象。
weak_ptr可以通过lock()函数来提升为shared_ptr(类型转换)
例1
- #include<iostream>
- #include<memory>
- #include "cat.h"
- #include "cat.cpp"
- using namespace std;
-
-
- int main(int argc, char *argv[])
- {
- std::shared_ptr<Cat> p0 = make_shared<Cat>("bb");
- std::weak_ptr<Cat> p1 = p0;
- cout<<"p0 use_count: "<<p0.use_count()<<endl;
-
- std::shared_ptr<Cat> p2 = p1.lock();
- cout<<"p0 use_count: "<<p0.use_count()<<endl;
-
- return 0;
-
- }

输出如下,可以看出weak_ptr不会增加引用计数,将weak_ptr使用lock()函数转换后可以增加引用计数
- Constructor of Cat :bb
- p0 use_count: 1
- p0 use_count: 2
- Destructor of Cat :bb
解决循环依赖问题
更改cat.h代码如下
- #ifndef CAT_H
- #define CAT_H
- #include <string>
- #include <iostream>
- #include "cat.h"
- #include<memory>
- class Cat
- {
- public:
- Cat(std::string name);
- Cat() = default;
- ~Cat();
- void cat_info() const
- {
- std::cout << "cat info name: " << name << std::endl;
- }
- std::string get_name() const
- {
- return name;
- }
- void set_cat_name(const std::string& name)
- {
- this->name = name;
- }
- void set_friend(std::shared_ptr<Cat> c){
- m_friend = c;
- }
- private:
- std::string name{ "Mini" };
- std::shared_ptr<Cat> m_friend;
- };
- #endif

此时就需要用weak_ptr来解决上图中的问题了,我们需要修改cat.h代码,如下图
此时再去运行main函数,就发现问题已经解决了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。