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

Felcia ·
更新时间:2024-09-20
· 627 次阅读

  在程序当中,经常会用到一些共享对象。一个具有指针成员的类,如果发生复制行为,一个指针复制到另一个指针时,两个指针指向同一个对象。此时可以使用任一指针改变这个共享的对象。那么,如果一个指针删除了这个共享对象,那么另一指针成了悬垂指针,如果再对此对象进行操作时,会发生内存访问错误。而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接口来完成。



C++ c+ 指针 智能指针

需要 登录 后方可回复, 如果你还没有账号请 注册新账号