指引网

当前位置: 主页 > 编程开发 > C >

c++实现自动的管理内存块实例代码

来源:网络 作者:佚名 点击: 时间:2017-07-19 23:08
[摘要]  c++如何实现自动的管理内存?本文提供了两个实现实例,后面那适合做相对简单的内存块自动管理,了解这方面的朋友可以真看看。

本文将来解决这样一个问题:复制一个对象的意义是什么?

假如一个对象的副本是一个完全不同的对象,这个副本具有原先对象的所有属性。然而,如果一个对象指向另一个对象的话,这个问题就变得更复杂:如果一个对象x指向一个对象y,那么复制x的时候是否也应该复制对象y呢?有时候这个问题很明显:如果y是x的一个成员,我们就必须在复制x的时候也复制y,如果x仅仅是一个指针,搞好指向y,那么我们就不需要复制y。

在这里我们定义了三种指针一样的类,他们在如何定义复制操作上互不相同。

一、一种管理内存的句柄类:复制所指向的对象,每个对象都复制一个副本

#ifndef _GUARD_14_HANDLE_H_   
#define _GUARD_14_HANDLE_H_
/********************************************************************  
创建时间:       2012/03/30 20:11 
文件名称:       14_Handle.h 
文件作者:       jimacs  
===================================================================== 
功能说明:       一个封装的句柄类,与它管理的对象的类型完全无关  
*********************************************************************/  
#include <iostream>
#include "core_grad.h"
template <class T>
class Handle
{
public:
    Handle():p(0) {} //默认构造函数
    Handle(const Handle& s):p(0) {
        if (s.p)
        {
            p=s.p->clone();
        }
    }
    Handle& operator= (const Handle& rhs)
    {
        if(&rhs!=this){
            delete p;
            p=rhs.p ? rhs.p->clone():0;
        }
        return *this;
    }
    ~Handle() {delete p;}
    Handle(T* t):p(t) {} //单参数构造函数,与实际的对象绑定
    operator bool () const {return p;} //类型转化操作符
    T& operator*() const
    {
        if (p)
        {
            return *p;
        }
        throw runtime_error("unbond Handle");
    }    
    T* operator->() const{
        if (p)
        {
            return p;
        }
        throw runtime_error("unbone Handle");
    }
    //static bool compare_core_handles(const Handle& h1,const Handle& h2)
    //{
    //    return h1->name()<h2->name();
    //}
private:
    T* p;
};
////赋值操作符
//template <class T>
//Handle<T>& Handle<T>::operator =(const Handle& rhs)
//{
//    if(&rhs!=this){
//        delete p;
//        p=rhs.p ? rhs.p->clone():0;
//    }
//    return *this;
//}
//
////*
//template <class T>
//T& Handle<T>::operator * () const 
//{
//    if (p)
//    {
//        return *p;
//    }
//    throw runtime_error("unbond Handle");
//}    
//template <class T>
//T* Handle<T>::operator ->() const
//{
//    if (p)
//    {
//        return p;
//    }
//    throw runtime_error("unbone Handle");
//}
#endif


下面通过一个实例与一个学生信息类相结合,他们彼此独立,只是通过上面的句柄类来管理此类的指针。


#ifndef _GUARD_14_STUDENT_INFO_H_   
#define _GUARD_14_STUDENT_INFO_H_
/********************************************************************  
创建时间:       2012/03/31 9:58 
文件名称:       14_Student_info.h 
文件作者:       jimacs  
===================================================================== 
功能说明:       重新实现Student_info类,而handle作为纯粹的接口类,把管理
                指针的工作委托给Handle类来实现  
*********************************************************************/  
#include <iostream>
#include <stdexcept>
#include "14_Handle.h"
#include "core_grad.h"
class Student_info
{
public:
    Student_info() {}
    Student_info(std::istream& is) {read(is);}
    //这里没有个函数,不需要了,都交给Handle类来完成
    std::istream& read(std::istream&); //read
    std::string name() const
    {
        if (cp)
        {
            return cp->name();
        }
        else
        {
            throw std::runtime_error("uninitialized student");
        }
    }
    double grade() const
    {
        if (cp)
        {
            return cp->grade();
        }
        else
        {
            throw std::runtime_error("uninitialized student");
        }
    }
    static bool compare(const Student_info& s1,const Student_info& s2)
    {
        return s1.name()<s2.name();
    }
private:
    Handle<core> cp;
};
#endif
/********************************************************************  
创建时间:       2012/03/31 10:15 
文件名称:       14_Student_info.cpp 
文件作者:       jimacs  
===================================================================== 
功能说明:       重新实现的Student_info类 
---------------------------------------------------------------------  
其他说明:       被Handle接口类托管 
*********************************************************************/
#include "14_Student_info.h"
using namespace std;
istream& Student_info::read(std::istream& is)
{
    cout<<"Inter who('c'or other):";
    char ch;
    is>>ch;
    if('c'==ch)
    {
        cp=new core(is);
    }
    else
    {
        cp=new grad(is);
    }
    return is;
}
//此函数与以前的Student_info函数相比,缺少了delete语句,为什么?
//首先看new core(is)语句:首先我们会得到一个core*对象,然后我们使用Handle类
//的Handle(T*)构造函数来隐式的把他转化为Handle<core>类型的对象(对象),然后会使用
//Handle类的赋值操作符把这个对象赋值给Student_info中得cp(这个过程很复杂:因为
//赋值构造函数中有delete自动删除cp原先指向的对象,所以我们这里不需要delete;还有一点:
//这里会调用赋值操作符中得conle函数,生成一个额外的指向原先core*对象的副本,
//这个副本又会产生一个Handle<core>对象(对象))---这个额为的副本如何避免,引用计数句柄
/********************************************************************  
创建时间:       2012/03/30 21:01 
文件名称:       14_Handle_main_1.cpp--主函数
文件作者:       jimacs  
===================================================================== 
功能说明:       使用一个泛型句柄 
---------------------------------------------------------------------  
其他说明:       14_Handle.h  
*********************************************************************/
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <ios>
#include <stdexcept>
#include <iomanip>
#include "14_Handle.h"
#include "core_grad.h"
using namespace std;
static bool compare_core_handles(const Handle<core>& h1,const Handle<core>& h2)
{
    return h1->name()<h2->name();
}
int main() 
{
    vector< Handle<core> > student;
    Handle<core> record;
    char ch;
    string::size_type maxlen=0;
    while (cin>>ch)
    {
        if ('c'==ch)
        {
            record=new core;
        }
        else
        {
            record =new grad;
        }
        record->read(cin);
        maxlen=max(maxlen,record->name().size());
        student.push_back(record);
    }
    //比较函数也可以作为handle类的静态函数,也可以作为全局函数,都可以
    sort(student.begin(),student.end(),/*Handle<core>::*/compare_core_handles);
    for (vector< Handle<core> >::size_type i=0;i!=student.size();++i)
    {
        cout<<student[i]->name()
            <<string(maxlen+1-student[i]->name().size(),' ');
        try{
            double final_grade=student[i]->grade();
            streamsize pre=cout.precision();
            cout<<setprecision(3)
                <<final_grade
                <<setprecision(pre)<<endl;
        }catch (domain_error e1){
    
        cout<<e1.what()<<endl;}
    }
    return 0;
}
/********************************************************************  
创建时间:       2012/03/31 11:02 
文件名称:       14_Student_info_main.cpp--主函数
文件作者:       jimacs  
===================================================================== 
功能说明:       用新建的_Student_info头文件重新实现主函数 
---------------------------------------------------------------------  
其他说明:       Handle类管理底层数据 
*********************************************************************/
#include <iostream>
#include <vector>
#include "14_Student_info.h"
using namespace std;
int main() 
{
    vector<Student_info> student;
    Student_info record;
    //同上面
    return 0;
}


二、引用计数句柄

我们通过实现引用计数句柄,避免了上面句柄类复制时副本的产生。但是仍然有问题,这种句柄当我们改变其中一个对象的值,我们就会改变其他同地址的其他对象的值。


#ifndef _GUARD_14_REF_HANDLE_CLASS_H_   
#define _GUARD_14_REF_HANDLE_CLASS_H_
/********************************************************************  
创建时间:       2012/03/31 11:18 
文件名称:       14_Ref_handle_class.h 
文件作者:       jimacs  
===================================================================== 
功能说明:       我们会为Ref_handle类添加一个指针来跟踪计数,Ref_handle所
            指向的每个对象都会有一个相关的引用计数,来跟踪记录我们有多少个这个对象的副本
             不管需不需要这个类都避免数据的复制
*********************************************************************/  
#include <iostream>
#include <stdexcept>
template <class T>
class Ref_handle
{
public:
    Ref_handle():p(0),refptr(new std::size_t(1)) { }
    Ref_handle(T* t):p(t),refptr(new std::size_t(1)) { }
    //三位一体
    Ref_handle(const Ref_handle& h):p(h.p),refptr(h.refptr)
    {
        ++*retptr;
    }
    Ref_handle& operator=(const Ref_handle&);
    ~Ref_handle();
    //as before Handle class
    operator bool () const {return p;}
    T& operator* () const
    {
        if (p)
        {
            return *p
        }
        throw runtime_error("unbound Ref_handle");
    }
    T* operator->() const
    {
        if (p)
        {
            return p;
        }
        throw runtime_error("unbound Ref_handle");
    }
private:
    T* p;
    std::size_t* refptr;
};
template <class T>
Ref_handle<T> Ref_handle<T>::operator =(const Ref_handle& rhs)
{//左操作数减之前,右操作数加。如果两个操作数指向同一个对象,这种做法即保证引用计数不变,
    //同时会保证引用计数不小心成为零。
    ++*rhs.refptr;
    if (0==--*refptr)
    {//如果应用计数减后成为零,说明左操作数赋值前是指向底层对象的最后一个Ref_handle。
        delete p;
        delete refptr;
    }
    p=rhs.p;
    refptr=rhs.refptr;
    return *this;
}
template <class T>
Ref_handle<T>::~Ref_handle()
{
    if (0==--*refptr)
    {
        delete p;
        delete refptr;
    }
}
#endif


三、可以决定何时共享数据的句柄类

上面两种方案,都不是很好,我们应该避免不必要的数据复制,但是也不能像上面一样,不管是否需要,这个类都会避免数据复制。

因此我们采取一种策略:只有在我们将要改变对象的内容,同时还有其他句柄指向这个对象的时候,我们的句柄类才会复制这个对象。

#ifndef _GUARD_14_PTR_FINAL   
#define _GUARD_14_PTR_FINAL 
/********************************************************************  
创建时间:       2012/03/31 16:17 
文件名称:       14_ptr(final handle).h 
文件作者:       jimacs  
===================================================================== 
功能说明:       最终的句柄类ptr,可以决定何时共享数据的句柄  
*********************************************************************/  
#include <iostream>
template <class T>
class Ptr
{
public:
    void make_unique()
    {
        if (*refptr != 1)
        {
            --*refptr;
            refptr = new std::size_t(1);
            //p=p?p->clone():0; //这里存在一个严重的问题,在Str_improvement中。
                              //因为clone只能作为类的成员函数。万一类中没有呢?
            p=p?clone(p):0; //中间函数来解决
        }
    }
    Ptr():p(0),refptr(new std::size_t(1)) { }
    Ptr(T* t):p(t),refptr(new std::size_t(1)) { }
    Ptr(const Ptr& h):p(h.p),refptr(h.refptr)
    {
        ++*retptr;
    }
    Ptr& operator=(const Ptr&);
    ~Ptr();
    //as before Handle class
    operator bool () const {return p;}
    T& operator* () const
    {
        if (p)
        {
            return *p
        }
        throw runtime_error("unbound Ref_handle");
    }
    T* operator->() const
    {
        if (p)
        {
            return p;
        }
        throw runtime_error("unbound Ref_handle");
    }
private:
    T* p;
    std::size_t* refptr;
};
template <class T>
Ptr<T>& Ptr<T>::operator =(const Ptr& rhs)
{//左操作数减之前,右操作数加。如果两个操作数指向同一个对象,这种做法即保证引用计数不变,
    //同时会保证引用计数不小心成为零。
    ++*rhs.refptr;
    if (0==--*refptr)
    {//如果应用计数减后成为零,说明左操作数赋值前是指向底层对象的最后一个Ref_handle。
        delete p;
        delete refptr;
    }
    p=rhs.p;
    refptr=rhs.refptr;
    return *this;
}
template <class T>
Ptr<T>::~Ptr()
{
    if (0==--*refptr)
    {
        delete p;
        delete refptr;
    }
}
template <class T> T* clone(const T* tp)
{ //通过这个函数来调用clone
    return tp->clone();
}
//模板特化(template specialization)
template <>
Vec<char>* clone(const Vec<char>* vp)
{
    return new Vec<char>(*vp);
}
#endif


这里上面提到了一个严重的问题,就是刚开始的clone函数,这个本来是在core类中定义的一个成员函数。从上面注释掉得一行,也可以看出,clone必须是Ptr所关联的类的成员函数,但是我们通过上面的句柄类改进以前面博文一个简单的string类Str中的Str类(因为上篇中得类会产生很多临时对象),如下:

#ifndef _GUARD_14_STR_IMPROVEMENT_H_   
#define _GUARD_14_STR_IMPROVEMENT_H_
/********************************************************************  
创建时间:       2012/03/31 19:22 
文件名称:       14_Str_improvement.h 
文件作者:       cjm  
===================================================================== 
功能说明:       原先Str类的一个改进,通过最终的句柄类Ptr   
*********************************************************************/  
#include <iostream>
#include <cstring>
#include <algorithm>
#include "14_ptr(final handle).h"
#include "Vec.h"
class Str
{
    friend std::istream& operator >> (std::istream&,Str&);
public:
    Str& operator+=(const Str& s)
    {
        data.make_unique();
        std::copy(s.data->begin(),s.data->end(),std::back_inserter(*data));
        return *this;
    }
    typedef Vec<char>::size_type size_type;
    Str():data(new Vec<char>) {}
    Str(const char* cp):data(new Vec<char>)
    {
        std::copy(cp,cp+std::strlen(cp),std::back_inserter(*data));
    }
    Str(size_type n,char c):data(new Vec<char>(n,c)) {}
    template <class In> Str(In i,In j):data(new Vec<char>)
    {
        std::opy(i,j,std::back_inserter(*data));
    }
    char& operator[](size_type i)
    {
        data.make_unique();
        return(*data)[i];
    }
    const char& operator[](size_type i) const
    {
        return (*data)[i];
    }
    size_type size()const {return data->size();}
private:
    Ptr< Vec<char> > data;
};
std::ostream& operator<<(std::ostream& ,const Str&);
Str operator+(const Str&,const Str&);
#endif


这里的make_unique函数中得clone,按照上面的情况应该属于Vec类,但是这是不可以的。因为这是实现的Vec类(参考c++模板函数声明定义分离编译错误详解)实际上是标准vector的一个子集,因为这个成员是标准所没有的,所以我们不应该加入Vec类中。那我们应该怎么办?

对于这个问题,我们所采用的解决方案常常是使用软件工程的基本原理:所有的问题都可以通过引用一个额外的中间转换来完成。

所以我们建立了一个全局函数:

template <class T> T* clone(const T* tp)
{ //通过这个函数来调用clone
    return tp->clone();
}


并且改变make_unique成员,使它调用新的clone函数:

void make_unique()
{
    if (*refptr != 1)
    {
        --*refptr;
        refptr = new std::size_t(1);
        //p=p?p->clone():0; //这里存在一个严重的问题,在Str_improvement中。
                          //因为clone只能作为类的成员函数。万一类中没有呢?
        p=p?clone(p):0; //中间函数来解决
    }
}



但是还是没有解决上面vec的问题,此时我们建立一个模板特化(template specialization)来解决。

//模板特化(template specialization)
template <>
Vec<char>* clone(const Vec<char>* vp)
{
    return new Vec<char>(*vp);
}


当把一个Vec<char>*参数传递给clone函数是,编译器会使用clone函数的特化版本,当传递其他类型的指针式,班一起就会实例化clone函数的通用版本。

ps:上面提到的core类的实现(包括派生的grad类)如下:

#ifndef GUARE_CORE_GRAD_H
#define GUARE_CORE_GRAD_H
#include <iostream>
#include <vector>
#include <string>
class core
{
    friend class Student_info_handle;
    template <class T> friend class Handle;
public:
    core():midterm(0.0),final(0.0) {}; //默认构造函数
    core(std::istream& is) { read(is); }; //单参数构造函数
    
    std::string name() const; //获得名字
    virtual std::istream& read(std::istream&); //读取信息,虚函数
    virtual double grade() const;               //平均成绩,虚函数
    virtual ~core() {}   //虚的析构函数:可以用基类的指针销毁派生类的对象
private:
    std::string n;
protected:
    std::istream& read_common(std::istream&);
    double midterm,final;
    std::vector<double> homework;
    //虚函数,克隆一个指针,创建一个对象:分配空间,使用core默认的复制构造函数来构造新对象
    virtual core* clone() const { return new core(*this);} 
};
class grad:public core
{
public:
    grad():thesis(0.0) {};
    grad(std::istream& is) { read(is); };
    double grade( ) const;
    std::istream& read(std::istream&);
protected:
    grad* clone() const { return new grad(*this); }
private:
    double thesis;
};
bool compare_new(const core& c1, const core& c2); //比较两个对象的name的大小,平常
bool compare_new_core_ptr(const core* c1,const core* c2); //重新定义比较函数,从而从引用变为指针的形式(非重载);
bool compare_grades(const core& c1, const core& c2); //动态绑定
//bool compare_grades(core c1,core c2) //静态绑定到core::grade上(编译时就会绑定)
//{
//    return c1.grade()<c2.grade();
//}
#endif
#include "core_grad.h"
#include "4_grade.h"
#include "4_Student_info.h"
#include <algorithm>
using namespace std;
//cord
string core::name() const
{
    return n;
}
double core::grade() const 
{
    return ::grade(midterm,final,homework);
}
istream& core::read_common(std::istream& in)
{   
    cout<<"输入学生姓名,期中,期末:"<<endl;
    in>>n>>midterm>>final;
    return in;
}
istream& core::read(std::istream& in)
{
    read_common(in);
    read_hw(in,homework);
    return in;
}
//class grad
istream& grad::read(istream& in)
{
    read_common(in); //保护可以直接使用
    cout<<"输入论文成绩:"<<endl;
    in>>thesis;
    read_hw(in,homework); //homework保护,可以直接使用
    return in;
}
double grad::grade() const
{
    return min(core::grade(),thesis); //这里生存空间操作符很重要,要不然就递归调用grad
                                      //版本的grade了。。。。。
}
bool compare_new( const core& c1, const core& c2 ) /*比较两个对象的name的大小,平常*/
{
    return c1.name()<c2.name();
}
bool compare_grades( const core& c1, const core& c2 ) /*动态绑定*/
{
    return c1.grade()<c2.grade();//怎么区分调用的事基类还是派生类中得grade呢,虚函数登场
}
bool compare_new_core_ptr( const core* c1,const core* c2 ) /*重新定义比较函数,从而从引用变为指针的形式(侵卦兀?*/
{
    return compare_new(*c1,*c2);
}


 
C++实现简单的内存块自动管理

#ifndef __MEM__H
#define __MEM__H
#include<iostream>
using namespace std;
//自动管理内存块
typedef unsigned char byte;
class Mem
{
private:
    byte* mem;
    int size;
    void ensureMinSize(int minSize);
public:
    Mem();
    Mem(int sz);
    ~Mem();
    int msize();
    //byte* pointer();
    byte* pointer(int minSize=0);
};
Mem::Mem()
{
    mem =0;
    size =0;
}
Mem::Mem(int sz)
{
    mem =0;
    size =0;
    ensureMinSize(sz);
}
Mem::~Mem()
{
    delete []mem;
}
int Mem::msize()
{//当前的内存大小
    return size;
}
void Mem::ensureMinSize(int minSize)
{//自动增长申请内存
    if(size < minSize)
    {
      byte* newmem = new byte[minSize];
      memset(newmem+size,0,minSize - size);
      memcpy(newmem,mem,size);
      delete []mem;
      mem = newmem;
      size = minSize;
    }
}
//byte* Mem::pointer()
//{//返回当前内存的首地址
    //return mem;
//}
byte* Mem::pointer(int minSize)
{//重新增长内存并返回内存的起始地址
      ensureMinSize(minSize);
      return mem;
}
#endif
#include "mem.h"
class Mystring
{
public:
    Mystring();
    Mystring(char *str);
    ~Mystring();
    void concat(char *str);
    void print(ostream &os);
private:
    Mem *buf;
};
Mystring::Mystring()
{
    buf = 0;
}
Mystring::Mystring(char *str)
{
    buf = new Mem(strlen(str)+1);
    strcpy((char*)buf->pointer(),str);
}
void Mystring::concat(char *str)
{
    if(!buf)
        buf = new Mem;
    strcat((char*)buf->pointer(buf->msize()+strlen(str)),str);
}
void Mystring::print(ostream &os)
{
    if(!buf)
        return ;
    os<<buf->pointer()<<endl;
}
Mystring::~Mystring()
{
    delete buf;
}
void main()
{
  Mystring s("mytest");
  s.print(cout);
  s.concat(" is weikai");
  s.print(cout);
}


尽量不要把默认参数作为一个标志去决定执行函数哪一块,这是基本原则.在这种情况下,只要能够就应该把函数分解成两个或者多个重载的函数。下面这种方式就是把默认参数作为决定执行

Mystring::Mystring(char *str)  
{  
  if(! *str)  
{  
  buf =0;  
return;  
}  
  
buf = new Mem(strlen(str)+1);  
strcpy((char*)buf->point(),str);  
}


------分隔线----------------------------