博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读书笔记:C++中利用智能指针和值型类防止内存非法访问
阅读量:6815 次
发布时间:2019-06-26

本文共 2734 字,大约阅读时间需要 9 分钟。

 读书笔记:C++中利用智能指针和值型类防止内存非法访问

2013-03-10 18:18 by DVwei, 138 阅读, 0 评论, 收藏, 编辑
在程序当中,经常会用到一些共享对象。一个具有指针成员的类,如果发生复制行为,一个指针复制到另一个指针时,两个指针就指向同一个对象。此时就可以使用任一指针改变这个共享的对象。那么,如果一个指针删除了这个共享对象,那么另一指针就成了悬垂指针,如果再对此对象进行操作时,就会发生内存访问错误。而C++中无法判断一个指针所指向的内存是否有效,这是非常危险的。
看下面一个例子:
复制代码
class MyClass
{
    public:
        MyClass(int *p,int i): ptr(p),value(i) { }
        ~MyClass() { delete ptr; }
        int get_share_value() { return *ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr = i; }
        void set_value(int i) { value = i; }
    private:
        int *ptr;
        int value;
};
int main()
{int *p  = new int(10);
    MyClass test(p,20);
    MyClass test2(test);
    test.get_share_value();//return 10
    test2.get_share_value();//return 10
    
    test2.set_share_value(110);
    test.get_share_value();//return 110
    
    test.set_share_value(120);
    test2.get_share_value();//return 120
    
    return 0;
}
复制代码
可以看到,使用任一指针都能改变共享的对象。而且运行这段代码就能发现程序结束的时候发生错误。这是因为,程序结束时,编译器会调用test和test2的析构函数,这样就发生了两次delete ptr,第一次delete释放掉val,第二次delete时,指针所指向的内存已经无效,所以就会发生内存访问错误。
为了避免出现重复delete同一对象的情况,可以定义智能指针来管理共享的对象。所谓智能指针,其实是引入一个使用计数,并建立一个计数类,用来记录目前指向同一对象的指针数目,确保只剩下最后一个指向对象时才删除对象。
代码如下:
复制代码
#include <iostream>
using namespace std;
class MyClass;
class Use_Ptr
{
    friend class MyClass;
    int *ptr;
    size_t use;
    Use_Ptr(int *p): ptr(p), use(1) { }
    ~Use_Ptr() { delete ptr; }
};
class MyClass
{
    public:
        MyClass(int *p,int i): ptr(new Use_Ptr(p)), value(i) { }
        //发生复制行为时,记录使用次数
        MyClass(const MyClass &mc): ptr(mc.ptr), value(mc.value) { ++ptr->use; }
        ~MyClass() { if(--ptr->use == 0) delete ptr; }//撤销对象前检查使用计数是否为零,若为零,撤销对象
        MyClass& operator=(const MyClass&);
        int get_share_value() { return *ptr->ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr->ptr = i; }
        void set_value(int i) { value = i; }
    private:
        Use_Ptr *ptr;
        int value;
};
MyClass& MyClass::operator=(const MyClass &mc)
{
    ++mc.ptr->use;//使用use之前先将sm.ptr->use加一,防止对自身赋值
    if(--ptr->use == 0)
      delete ptr;
    ptr = mc.ptr;
    return *this;
}
int main()
{
    int *p = new int(10);
    MyClass test(p,20);
    MyClass copy(test);
    return 0;
}
复制代码
这样就确保了共享的对象在没有指针指向它的时候才被delete掉,也就不会发生内存的非法访问了。
另一种方法是把类设计成和值类型具有相同的复制行为,即复制类对象的同时复制指针指向的值,而不是只复制指针的值。于是可以把上述MyClass这样定义:
复制代码
class MyClass
{
    public:
        MyClass(int &p,int i): ptr(new int(p)), value(i) { }
        MyClass(const MyClass &mc): ptr(new int(*mc.ptr)), value(mc.value) { }//把值也复制
        ~MyClass() { delete ptr; }//这里可以直接释放
        MyClass& operator=(const MyClass&);
        int get_share_value() { return *ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr = i; }
        void set_value(int i) { value = i; }
    private:
        int *ptr;
        int value;
};
MyClass& MyClass::operator=(const MyClass &mc)
{
    *ptr = *mc.ptr;
    value = mc.value;
    return *this;
}
复制代码
这样每个指针都指向一个值相同但相互独立的对象,delete后也不会影响到其他的对象。
这种方法也叫深度复制,在C#中可以通过实现clone接口来完成。

转载地址:http://hdbzl.baihongyu.com/

你可能感兴趣的文章
Android 通过HTTPCLINET POST请求互联网数据
查看>>
Hadoop集群的配置(一)
查看>>
Kafka 学习笔记之 Consumer API
查看>>
教程-Close、Halt、terminate、ExitProcess的区别
查看>>
10款很好用的 jQuery 图片滚动插件
查看>>
teacher forcing
查看>>
Linux命令小记
查看>>
基于ROS和beaglebone的串口通信方式,使用键盘控制移动机器人
查看>>
android.view.WindowLeaked的解决办法
查看>>
存储过程的笔记
查看>>
OpenCV学习(27) 直方图(4)
查看>>
深度学习原理与框架-Tensorflow基本操作-实现线性拟合
查看>>
[leetcode-168-Excel Sheet Column Title]
查看>>
SpringBoot和数据库连接
查看>>
二叉搜索树
查看>>
网页小技巧-360doc个人图书馆复制文字
查看>>
delete删除-some
查看>>
maven阿里云中央仓库
查看>>
15.12.14listbox列表框
查看>>
sql 行转列
查看>>