• C++智能指针: auto_ptr, shared_ptr, unique_ptr, weak_ptr


    本文参考C++智能指针简单剖析

    内存泄露

    我们知道一个对象(变量)的生命周期结束的时候, 会自动释放掉其占用的内存(例如局部变量在包含它的第一个括号结束的时候自动释放掉内存)

    int main () {
    	{
    		int a = 1;
    		printf("%d
    ", a);
    	}
    	{
    		a = 2;
    		printf("%d
    ", a);
    	}
    }
    

    这样会编译错误.

    但是如果这样写呢?

    void func(int &o) {
    	int *p = new int(o);
    	return;
    }
    

    程序结束的时候会自动释放p的内存, 但是由new算符创建的匿名变量却一直留在内存中, 这就为内存泄露留下了隐患.
    所以在程序结尾要加上delete p, 释放掉p指向的变量占用的内存.

    那么能不能在指针过期的时候自动释放掉它指向的内存呢?
    可惜它不是具有析构函数的类对象指针.
    如果这个指针本身就是个对象, 它就可以实现在指针本身离开作用域的时候释放其指向的内存的析构函数了.

    这就是auto_ptr, shared_ptr, unique_ptr几个智能指针背后的设计思想.可以理解为这个指针本身就是一个对象, 因为它具有对象的行为.

    可以将上述有问题的代码改写为这样:

    void func(int &o) {
    	auto_ptr<int> p(new int(o));
    }
    

    这样在函数结束的时候内存就会被释放了.

    智能指针

    这个东西是在C++98提供的.C++11已经抛弃了它并且提供了另外两种解决方案.
    如果没有C++11的话就只能使用这个东西了.

    不允许隐形转换

    所有的智能指针都有一个explicit构造函数, 以指针为参数.

    这个东西大概可以这样写

    template <class T>
    class auto_ptrs {
    private:
    	T* ptr;
    public:
    	explicit auto_ptrs (T* p = 0): ptr(p) {}
    	~auto_ptrs() { delete ptr; printf("delete
    "); }
    };
    int main () {
    	{
    		auto_ptrs<int> p (new int(1));
    		auto_ptr<int> ptr, pt(new int(2));
    		ptr = auto_ptr<int>(new int(3));
    	}
    }
    

    只能用于堆内存

    int main() {
    	int a = 0;
    	std:: auto_ptr<int> p(&a);
    }
    

    这样写是错误的, 因为它会在main&a执行delete操作, 而a位于栈内存, 不能被delete.

    unique_ptr代替auto_ptr

    如果将一个指针赋值给另一个指针呢?

    class Int{
    	int a;
    public:
    	int J() {return a;}
    	Int(int _) : a(_) {} 
    	~Int() { printf("delete
    "); }
    };
    int main() {
    	auto_ptr<Int> p (new Int(123456789));
    	auto_ptr<Int> q;
    	q = p;
    }
    

    如果在p, q作用域结束的时候分别delete一次, 那么就会delete同一个变量两次, 这样是不行的.

    有很多种解决方案可以解决这个问题:

    • 深度复制, 就是将指针指向的内存复制一遍再让另一个指针指向它.
    • 转让所有权, 由于一个对象只能由一个对象所拥有, 另一个就变空指针了, 只让拥有对象的指针删除该对象.(auto_ptrunique_ptr的策略)
    • 跟踪引用特定对象的智能指针数.——引用计数(shared_ptr的策略), 如果一个对象被引用了p次, 那么得当指针全部销毁时对象才会销毁.

    但是用auto_ptr还是有一个问题, 如果q = p之后又调用了*p, 对一个空指针解除引用程序是会崩溃的.
    unique_ptr的好处是如果你这样写

    unique_ptr<Int> p (new Int(1));
    unique_ptr<Int> q;
    q = p;
    

    你会得到一个编译错误, 原因是避免潜在的内存崩溃问题.
    但是如果你把一个临时的unique_ptr赋值给unique_ptr却不会出现问题, 这是它聪明的地方.
    例如

    unique_ptr<int> func(int &a) {
    	unique_ptr<int> p(&a);
    	return p;
    }
    

    或者

    unique_ptr<int> a;
    a = unique_ptr<int> (new int(199));
    

    你甚至可以将unique_ptr显性转化为shared_ptr, 当然对象也被接管.

  • 相关阅读:
    牛客网Java刷题知识点之方法覆盖(方法重写)和方法重载的区别
    牛客网Java刷题知识点之自动拆装箱
    安装Phoenix时./sqlline.py执行报错File "./sqlline.py", line 27, in <module> import argparse ImportError: No module named argparse解决办法(图文详解)
    Apache-kylin-2.0.0-bin-hbase1x.tar.gz的下载与安装(图文详解)
    Apache Kylin的架构特性
    Apache Kylin Cube 的存储
    Apache Kylin Cube 的构建过程
    Apache Kylin的核心概念
    中央网络安全和信息化领导小组办公室
    中国智慧城市建设投资联盟
  • 原文地址:https://www.cnblogs.com/qdscwyy/p/9830139.html
Copyright © 2020-2023  润新知