大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++中的指针(三)智能指针

C++中的指针(三)智能指针

关键词:智能指针C++指针  阅读(559) 赞(15)

[摘要]本文是对C++中的指针(三)智能指针的讲解,对学习C++编程技术有所帮助,与大家分享。

Smart Pointer是C++中的一个大标题,要说清楚他的一切益处很需求费点力气。我就一个功用一个功用的说。有我了解不透的中央希望大家指点。

1.copy-to-write
当生成一个C++ object的时分假定这个class很大,这个object会占用很多空间。那么每生成一个就占用一片空间,这样会占用很多零碎资源。同时降低效率。一个处置办法就是对用拷贝结构函数生成的object,让他不存储数据,而只存储一个指向原来object数据的指针。这样空间就节省了很多。但效果在于这样两个object完全结合在了一同。假定修正了其中一个,另一个也跟着变了。所以这种办法不可取。这里讲的copy-to-write技术就是处置这类效果的办法。当经过援用一个已有object去拷贝结构新object时,新object只需一个指向已有object的指针。这两个object共享数据。直到其中一个需求修正数据的时分,再把这两块数据区分。这里举一个最简化的例子。假定一个class叫CLargeObject,外面存有很少数据。我们用一个inner class来把一切数据放在一同,叫CData。CData外面存有大批数据,例如一个数据库。这里用最复杂的模型来表示,假定只需一个整数int m_nVal; CData外面需求包括另一个变量。叫作索引数目(reference count)。它记载了指向这个CData object的来自CLargetObject类的指针各数。也就是说,总共有多少CLargeObject的object正在援用着以后的CData object。

class CLargeObject
{
private:
    struct CData
    {
    private:
        int m_nVal;
        int m_nReferenceCount;
    }
};

关于每个CLargeObject的object,我们用一个CData类的指针来指向其数据。
CData *m_pData;

CLargeObject至多有两个结构函数。第一个是规范的结构函数,初始化其数据。这时数据是独一的,所以必需重生成一个CData的object来存储数据。
CLargeObject::CLargeObject(int nVal)
{
    m_pData = new Data(nVal);
}
而关于CData类的结构函数而言,初始化他的CLargeObject是第一个指向他的,这一时辰索引数目m_nReferenceCount是1。
CLargeObject::Data::Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}

CLargeObject的第二个结构函数是拷贝结构(copy constructor)。这样生成的object不需求有新的数据,和已有的object共享数据就可以了。这是索引数目需求加1。表示又有一个object指向以后的CData了。
CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
    ob.m_pData->m_nReferenceCount++;
    m_pData = ob.m_pData;
}


这样CLargeObject就结构好了,运用了可以的最少的内存。上面看看他的析够函数(destructor)。当一个object被delete的时分,它的数据不一定有效,假定别的object还在援用着这个数据,数据需求留上去。当然,数据的索引数目无论如何都要减1。
CLargeObject::~CLargeObject()
{
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
}

上面看一看赋值操作。先说用已有的CLargeObject赋值给这个CLargeObject。这时以后CLargeObject外面的数据要指向已有的这个object,就搞定了。
CLargeObject& CLargeObject::operator = (const CLargeObject& ob)    // copy assignment
{
    ob.m_pData->m_nReferenceCount++;
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
    m_pData = ob.m_pData;

    return *this;
}

再来看看如何对CLargeObject外面的数据中止真正的修正。这样就一定需求对以后的object独立操作了,否则就影响到了其它指向同一块数据的CLargeObject。这样CData类需求一个新的函数,生成只用于以后CLargetObject的数据。假定以后的援用数目是1,那么当然这个CData就是只用于这个CLargeObject的了。否则就重新new一个CData前往。
        Data* CLargeObject::CData::get_own_copy()    // clone if necessary
        {
            if (m_nReferenceCount==1)
                return this;
            m_nReferenceCount--;
            return new Data(m_nVal);
        }
CLargeObject修正前用这个函数失掉独一的object,然后对它赋值。
void CLargeObject::SetVal(int nNewVal)
{
    m_pData = m_pData->get_own_copy();
    m_pData->m_nVal = nNewVal;
}
关于一切可以改动CData值的操作,都需求用这种办法。

上面是只读函数,复杂。直接前往值,什么特殊的都不必作。
int CLargeObject::GetVal() const
{
    return m_pData->m_nVal;
}


这样copy-to-write技术就完成了。上面把残缺的顺序写一下:
class CLargeObject
{
public:
    CLargeObject(int nVal);
    CLargeObject(const CLargeObject &ob);
    ~CLargeObject();

    CLargeObject& operator = (const CLargeObject& ob);
    void SetVal(int nNewVal);
    int GetVal() const;
private:
    struct Data
    {
    public:
        Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}
    private:
        friend class CLargeObject;
        Data* get_own_copy()    // clone if necessary
        {
            if (m_nReferenceCount==1)
                return this;
            m_nReferenceCount--;
            return new Data(m_nVal);
        }

        // control variables.
        int m_nReferenceCount;
    
        // actual data portion
        int m_nVal;
    };

    Data *m_pData;
};

CLargeObject::CLargeObject(int nVal)
{
    m_pData = new Data(nVal);
}

CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
    ob.m_pData->m_nReferenceCount++;
    m_pData = ob.m_pData;
}

CLargeObject::~CLargeObject()
{
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
}

CLargeObject& CLargeObject::operator = (const CLargeObject& ob)    // copy assignment
{
    ob.m_pData->m_nReferenceCount++;
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
    m_pData = ob.m_pData;

    return *this;
}

void CLargeObject::SetVal(int nNewVal)
{
    m_pData = m_pData->get_own_copy();
    m_pData->m_nVal = nNewVal;
}

int CLargeObject::GetVal() const
{
    return m_pData->m_nVal;
}


很多存储数据的零碎class,如string,CString等都有这种设计。所以记住这个运用是很有必要的。



相关评论