Ownership of Container Object

      本文主要记录我在学习Data Structures and Algorithms with Object-oriented Design Patterns in C++ 第五章《Data types and Abstraction》时关于容器及容器内元素的理解。同时,也截取了《An Introduction to Design Patterns in C++ with Qt》第六章部分关于Qt容器的内容,进行比较。

一、容器(container)

      The Container class abstracts the notion of a container--an object that holds within it other objects.

      容器就是一种能存放其他物质的物质。参考:点击打开链接。具体的接口声明如下:

<span style="font-size:14px;">class Container : public virtual Object, public virtual OwnerShip
{
public:
    virtual unsigned int Count() const;
    virtual bool IsEmpty() const;
    virtual bool IsFull() const;
    virtual HashValue Hash() const;
    virtual void Put(std::ostream &)const;
    virtual Iterator & NewIterator() const;

    virtual void Purge() = 0;
    virtual void Accept(Visitor &) const = 0;
protected:
    Container();
    unsigned int count;
};</span>
      以上是容器的一些最基础的接口。其中,构造函数是protected类型,限定该类仅用作虚基类。此外,有两个函数需要特别注意,一是迭代器的工厂函数(NewIterator()函数),用于产生一个与具体容器匹配的迭代器(在具体的类中,会有一个迭代器成员);另一个是Accept()函数,用于接收一个外部传入的Visitor对象(这个Visitor模式,类似于函数指针作为函数形参的作用,即机制与策略分离)。下面在展示一个容器的一些扩展接口:

<span style="font-size:14px;">class SearchableContainer : public virtual Container
{
public:
    virtual bool IsMemeber(Object const &) const = 0;
    virtual void Insert(Object &) = 0;
    virtual void Withdraw(Object &) = 0;
    virtual Object & Find(Object const &) const = 0;
};</span>

二、Direct vs. Indirect Containment

       Direct Containment —— When an object is put into a container, a copy of that object is made in the container.

       Indirect Containment —— When an object is put into a container, a pointer to that object is kept in the container.

    以上可以翻译为“托管包含”和“非托管包含”。在Qt教材中,又称为组合(composite)和聚合(aggregate),其核心区别就是容器是否管理其元素。

    Direct Containment相对比较简单,适合值类型(指针另议),如内置类型(built-in)。

扩展:(参考Qt教材)C++类型可以分为两类,值类型(value)和对象类型(object)。值类型的实例通常相对简单,占用相邻的内存控件,而且可以进行快速复制或者比较。eg: int,char, QString, QDate和QVariant。带有公有默认构造函数、复制构造函数和赋值运算符的任何类都是值类型。相反,对象类型比较复杂,在Qt中,对象类型一般设计为不可复制,如QObject及其子类对象。

    Indirect Containment保存的是指针,相对来说能节省内存空间和减少拷贝,对于非内置类型,一般采用这种方式。(注:std::string和QString采用Direct Containment)。


三、Qt指针容器(参考Qt教材)

    Indirect Containment实际上实现的就是一种指针容器。指针是一种值类型,单个指针占用内存size_t,能够节省内存空间和提升操作效率(无须直接拷贝object)。此外,通过基类指针,还能利用多态特性。

    但是,设计指针容器的时候需要小心设计析构过程,以避免内存泄漏。此外,对指针的访问和维护必须小心控制,以防止解引用空指针或者未定义的指针。规则:

1,当向容器添加指针时,必须确保它已经立即初始化了。如果不方便初始化,则应将其赋值0(NULL,栓住)。

2,当某个指针不再需要时,应移走并删除它。如果不方便立即移走,则被删除的指针应被重新赋值或者设置为0。

3,销毁Qt指针容器时,应调用qDeleteAll()函数,它是针对全部Qt容器的通用算法。也可以调用具体容器的clear()函数。qDeleteAll(container)是一个对容器中的每一个元素调用delete的算法。


四、Ownership of Contained Objects

    针对指针容器,由于其存放的元素都是指针,究竟该由容器自动管理其元素指向的内存还是由外部user管理这些内存?

    如果由用户管理这些内存,则容器析构时,仅仅只需要将其包含的元素(指针)移出即可。上面所说的Qt指针容器采用的就是这种设计,故“销毁Qt指针容器时,应调用qDeleteAll()函数”。

    如果由容器自动管理这些内存,容器销毁时,将逐个对其元素(指针)调用delete。即,在容器的析构函数中,调用qDeleteAll(*this)。前提是,这种类型的容器,其元素(指针)必须指向动态分配的内存空间,而不是静态全局的内存空间或stack空间。

    故在此,引入了Ownership(所有权)的概念,让用户来指定指针容器如何管理其元素指向的内存。参考:点击打开链接

    从上面容器类的定义也可看出,容器既是一种Object,也有所有权。(多继承)

<span style="font-size:14px;">class OwnerShip
{
public:
    void AssertOwnership() { isOwner = true; }
    void RescindOwnership() { isOwner = false; }
    bool IsOwner() { return isOwner == true; }

protected:
    OwnerShip() : isOwner(true) {}
    OwnerShip(OwnerShip & arg) : isOwner(arg.isOwner)
    {
        arg.isOwner = true;
    }
private:
    bool isOwner;
};</span>
The Ownership class encapsulates a single Boolean variable, isOwner, which records whether the container is the owner of the contained objects.

The behavior of the copy constructor is subtle: It transfers ownership status from the original container to the copy. This behavior is useful because it simplifies the task of returning a container as the result of a function.
每个容器都有一项特性,即容器对它包含的元素的所有权。所有权也可以转移。

这样,就可以像下面的伪代码一样设计容器的函数实现

<span style="font-size:14px;">void SomeContainer::Purge()
{
    if (IsOwner())
	    for each Object i in this container
		    delete &i;
	Now clean up the container itself.
}</span>
这里定义的容器,默认是对其指针元素有Ownership的,而Qt指针容器没有。







你可能感兴趣的:(Ownership of Container Object)