C++实现垃圾回收机制

C++实现垃圾回收机制

       我想讨论的问题是,如何让下面的代码正确无误:

#include <iostream>

classHuman

{

public:

    Human()

    {

       std::cout << "Human" << std::endl;

    }

    ~Human()

    {

       std::cout << "~Human()" << std::endl;

    }

};

int main()

{

       Human*pHuman = new Human;

       system(“pause”);

       return0;

}

       有没有发现其中的错误,而且这个错误很严重,嗯……我想你发现了,没有delete pHuman回收指针所指的内存空间,怎么知道?起码Human的析构函数没有调用,这会造成内存泄漏,非常严重的问题。就想你借了人家钱而不还一样,当然,如果你借的钱少,那问题不至于严重到不可收拾的地步,但是你借了很多钱而不还,那可麻烦了。

       JavaC#AS这些语言中,上面的代码是没有任何问题的,因为你借了这些内存空间,在程序结束后,虚拟机会帮你还,也就是说,它们自带有垃圾回收机制,而C++?必须得手动delete,如果只是new出来一两个,手动delete还不觉得这么麻烦,但是如果你new出来很多东西,那怎么办,你得一个个记住,然后一个个delete?就像你借了很多人的钱,你必须记住谁谁借了钱给我,你这时会感叹,如果能有个人帮我还钱,那该多好啊,那样的话,你就只管借而不用担心钱没还而惹来的麻烦了。那在C++里,怎么实现呢?

       你可能会想到用智能指针来管理,嗯……很好的想法,很自然的,你会修改上面的代码:

       intmain()

       {

              std::auto_ptr<Human> pHuman(new Human);

              system(“pause”);

              return;

}

如果编译器说std里没有auto_ptr的话,#include<memory>就可以了,这就是智能指针的好处,只要你这个智能指针不是new创建出来的,它会答应帮你delete掉,如果你是new出来的,那么也得delete,否则它无法完成它的承诺。

也就是说要像下面那样:

int main()

{

              std::auto_ptr<Human>*pHuman  = new std::auto_ptr<Human>(new Human);

              delete pHuman;

              system(“pause”);

              return;

}

如果你new创建智能指针的话,问题又继续循环了,所以智能指针不是用new出来的,否则你也必须delete。也就是说你借了那个帮你还钱的人的钱,所以你得先把钱还给他,他才帮你还清其他的钱。

OK,你保证你使用智能指针不new创建出来,那事情是不是就完了呢?当然还没,使用std::auto_ptr还有其他要注意的问题,想知道详细的话,研究Effective C++More Effective C++便可以了解。但在这里不是我讨论的重点,我想讨论的是,智能指针的原理是什么?看下面代码便清楚了:

 

classHumanPtr

{

private:

    Human* m_ptr;

public:

    HumanPtr(Human* ptr = 0):m_ptr(ptr)

    {

    }

    ~HumanPtr()

    {

       if(m_ptr)

       {

           deletem_ptr;

           m_ptr = 0;

       }

    }

};

那么像下面那样子使用:

int main()

{

       HumanPtrpHuman(new Human);

       /.../

}

也就是只要把new创建出来的那个指针放到一个对象里保存,而这个对象不是使用new创建的,只要符合这个规则即可实现只需new创建,而不用delete

       那为什么会自动帮我delete呢?

       我们知道,每个对象都有一个生命周期,当生命结束后,系统会调用它的析构函数,然后回收它的内存空间。像上面那个HumanPtr pHuman,它的生命周期是整个程序运行时间,如果程序结束,它也结束,因为它是在栈里开辟空间的,在栈里开辟的空间,系统会自动回收掉,而在堆里开辟的,系统无法自动做到,如果能做到,也就没必要在这里讨论这么多了,标准库也就没必要弄出一个什么智能指针的东西了。

      因此,智能指针都应该是在栈里开辟空间。

当然std::auto_ptr比这个HumanPtr做的事情还要多,但是它便是基于这个思想的。但是,这样还是没有解决我的问题。

我想要的是下面的代码正确无误:

int main()

{

       Human* pHuman = new Human;

       system(“pause”);

       return0;

}

       现在的问题是,这段代码造成了内存泄漏,如何解决这个问题?

       嗯……要解决这个问题,只需调用delete pHuman就可以了,那能不能让系统帮我们调用呢?答案是可以的,看下面的代码:

//.h头文件

//对象

classObject

{

public:

    //自定义的operator new

    static void* operator new(std::size_t);

    //自定义的operator delete

    static void operator delete(void* pVoid);

 

    virtual~Object() = 0{};

};

//单体

classJSingleton

{

protected:

    JSingleton(){};

    virtual~JSingleton() = 0{};

private:

    JSingleton(constJSingleton&);

    JSingleton& operator=(const JSingleton&);

};

//工厂

classFactory:public JSingleton

{

protected:

    Factory(){};

    virtual~Factory();

private:

    std::vector<Object*> m_pObjects;

    friend class auto_ptr<Factory>;

    staticauto_ptr<Factory> s_factory;

public:

    staticFactory* Instance();

    //增加对象

    voidAddObject(Object* pObject)

    {

       m_pObjects.push_back(pObject);

    };

    //移除对象

    voidEraseObject(Object *pObject);

};

classHuman:public Object

{

public:

    Human()

    {

       std::cout << "Human" << endl;

    }

    ~Human()

    {

       std::cout << "~Human()" << endl;

    }

};

//.cpp文件

#include<memory>

#include<iostream>

#include<vector>

auto_ptr<Factory>Factory::s_factory;

Factory*Factory::Instance()

{

    //如果工厂没有创建

    if(s_factory.get() == 0)

    {

       s_factory.reset(newFactory);

    }

    returns_factory.get();

}

Factory::~Factory()

{

    //在工厂里保存的所有指针删除,回收空间

    for (DWORD i= 0; i < m_pObjects.size(); ++i)

    {

       if(m_pObjects[i])

       {

           ::deletem_pObjects[i];//调用全局的delete

       }

    }

}

//移除对象

voidFactory::EraseObject(Object *pObject)

{

    vector<Object*>::iterator iter =m_pObjects.begin();

    //遍历寻找对应的指针

    while (iter!= m_pObjects.end())

    {

       if (*iter== pObject)

       {

           iter = m_pObjects.erase(iter);//移除操作返回的是移除后的下一个

           ::operatordelete(pObject);//调用全局的delete

           return;

       }

       else

{

++iter;

}

    }

}

void*Object::operatornew(std::size_tst)

{

    Human *pHuman = (Human*)::operatornew(st);//调用全局的new

    Factory *pf = Factory::Instance();//获得唯一实例化的引用

    pf->AddObject(pHuman);//将创建后的指针放到工厂里保存

    returnpHuman;

}

void Object::operatordelete(void* pObject)

{

    Factory *pFactory = Factory::Instance();//获得唯一实例化的引用

    pFactory->EraseObject((Object*)pObject);//移除工厂中保存对象的指针

}

//main主函数

intmain()

{

Human *pHuman = new Human;

         system("pause");

    return 0;

}

         运行上面的代码,你会发现当程序结束后,Human的析构函数调用了,那Human的空间有没有回收掉呢?答案是肯定的,我测试过,是没问题的,你可以自己做一下测试,可以创建一万个Human出来,然后看一下内存,程序结束后,再看一下内存,需要注意的是程序结束后是看不到内存了,而内存的释放又是在程序结束后的,因此,你可以将工厂设置为可以delete掉的,可以提前结束它的生命,又或者是提供一个Clear函数接口来测试,那样就可以看到内存的变化了。这里的工厂用的是单体模式,保证只存在一份实例,如果对单体模式不了解的话,可以看下我的另一篇关于单体模式的文章。

       当然,这里只是提供一个思路和一份实现而已,你可以有你自己的一份实现,但是原理都差不多,就是把new创建出来的指针放到一个对象管理,这里采用的是工厂,这个对象要保证如果用户不自行delete,那么在程序结束后,这个delete工作将由这个对象执行。如果Human是一个抽象类,上面那个Factory也能正常工作。

       这里需要注意的是,在使用newdelete时的一些事情,当使用new实例化时,编译器做了两件工作:

1)首先在堆内存开辟了该类大小的空间,也就是说会先调用operator new(std::size_t)函数开辟空间,至于调用全局的还是这个类的,就要看这个类是否重载了operator new(std::size_t),如果这个类重载了,便调用该类提供的,编译器会调用sizeof(Human)计算出这个类的内存大小,然后作为实参传入到这个函数中,这个函数分配好空间了,返回这个内存空间的首地址。

2)分配好内存空间后,调用这个类的构造函数,上面调用的是默认构造函数。

       那么当delete的时候,编译器也做了两件事,是new相反顺序的,

1)首先调用这个对象的析构函数

2)再调用void operatordelete(void*)函数进行回收空间,至于调用全局

还是这个类提供的,就看这个类是否重载了这个函数。

关于operator new operator delete的信息,可以研究一下C++ Primer第四版,那里会有更详细的说明。

还得注意的是,这里采用的是vector顺序表来存储指针,如果你担心内存,可以采用list链表才存储,因为vectorerase移除操作并非是真正的释放空间,虽然你delete掉了pHuman的指针所指的内存,但是vector内部并没有收回掉那4个字节的内存,因为它要预留着下次来使用,那样就不需要再分配内存了。当然,你可以调用vector的析构函数来释放vector保存指针的那4个字节内存。

还有个问题是,如果自定义一个新类型时,必须得继承Object,不然得自行提供operator newoperator delete,否则无法自动回收内存。

可能还有其他的情况我没考虑到,如果发现,强烈希望指出。希望这个技术能对你有用。

这是最后源代码的链接http://download.csdn.net/detail/zx504287/4988003

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值