私有构造函数的作用

一、背景知识
对class本身来说,可以利用它的static共有成员,因为它们独立于class对象之外,不必产生对象也可以使用class的私有域。可以参考静态成员变量和静态成员函数在C++类中的作用来进行理解。

二、相关说明
构造函数被class私有化,需要创建对象就必须能够访问到class的私有域,static共有成员可以做到。假如在某个static函数中创建了该class的对象,并以引用或者指针的形式将其返回(这里不以对象返回,主要是构造函数是私有的,外部不能创建临时对象。),就获得了这个对象的使用权。

三、举例说明

#include 
using namespace std;

class OnlyHeapClass {
public:
    static OnlyHeapClass* GetInstance()
    {
        cout << "create object,return a pointer" << endl;
        return (new OnlyHeapClass);
    }
    void Destroy() 
    { 
        cout << "delete object" << endl;
        delete this; 
    }
private:
    OnlyHeapClass() {}
    ~OnlyHeapClass() {}
};

void main()
{
    OnlyHeapClass *p = OnlyHeapClass::GetInstance();//静态成员函数的访问

    p->Destroy();
    return;
}

这个例子使用了私有构造函数,GetInstance()作为OnlyHeapClass的静态成员函数在内存中创建对象,由于创建对象在堆上,即使GetInstance()退出,对象也不会随之释放,可以手动释放。

四、构造函数私有化的作用
构造函数私有化的类的设计保证了其他类不能从这个类派生或者创建类的实例。
此外,还有这样的用途:例如,实现这样一个class,它在内存中至多存在一个,或者指定数量个的对象(可以在class的私有域中添加一个static类型的计数器,它的初值置为0,然后在GetInstance()中作些限制,每次调用它时先检查计数器的值是否已经达到对象个数的上限值,如果是则产生错误,否则才new出新的对象,同时将计数器的值增1)。最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。
如果将构造函数设计成Protected,也可以实现同样的目的,但是可以被继承。

五、如何限定对象建立只能在堆上呢?
上述例子将析构函数定位为私有成员,也限定了对象建立只能在堆上。
原因是C++是一个静态绑定语言,在编译过程中,所有的非虚函数调用都必须分析完成,即使是虚函数,也需要检查可访问性。当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。

编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数,即析构函数是私有的,编译器无法调用析构函数来释放内存。所以上述例子类中必须提供一个Destroy函数,来进行内存空间的释放。类对象使用完成后,必须调用Destroy函数。

这里补充一点,delete操作会调用析构函数,所以不能编译通过。提供一个Destroy成员函数,完成delete操作。在成员函数中,析构函数是可以访问的,当然detele操作也是可以编译通过。

上述方法的一个缺点就是,无法解决继承问题。如果OnlyHeapClass作为其他类的基类,则析构函数通常要设为virtual,然后在子类重写,以实现多态,可以参照C++多态性(一)和C++多态性(二)来理解。因此析构函数不能视为private。将洗过函数设为protected可以有效解决这个问题,类外无法访问protected成员,子类则可以访问。

为了统一,可以将构造函数设为protected,然后提供一个public的static函数来完成构造,这样不用直接使用new而是使用一个函数来构造,使用一个函数来析构。

#include 
using namespace std;

class A {
public:
    static A* CreateInstance()
    {
        cout << "create object,return a pointer" << endl;
        return (new A);
    }
    void Destroy() 
    { 
        cout << "delete object" << endl;
        delete this; 
    }
protected:
    A() {}
    ~A() {}
};

void main()
{
    A *p = A::CreateInstance();

    p->Destroy();
    return;
}

六、相关问题讨论
1、什么情况下需要自己调用析构函数?
有人会说,对象生命周期结束后不就自己调用析构函数了吗,为何还需要手动调用。我们来看看这样一种情况,你希望在析构之前必须做一些事情,但是用你类的人并不知道,那么你就可以重新写一个函数,里面把要做的事情全部做完了再调用析构函数。 这样人家只能调用你这个函数析构对象,从而保证了析构前一定会做你要求的动作。

2、什么情况下需要只创建堆对象呢?
堆对象就是new出来的,相对于栈对象而言。什么情况下要new,什么情况下在栈里面 提前分配,无非就是何时该用动态,何时该用静态生成的问题。这个要根据具体情况具体分析。
比如你在一个函数里面事先知道某个对象最多只可能10个,那么你就可以定义这个对象的一个数组。10个元素,每个元素都是一个栈对象。如果你无法确定数字,那么你就可以定义一个这个对象的指针,需要创建的时候就new出来,并且用list或者vector管理起来。

3、类中“私有”权限的含义就是:私有成员只能在类域内被访问,不能在类域外进行访问。
把析构函数定义为私有的,就阻止了用户在类域外对析构函数的使用。这表现在如下两个方面:第一、禁止用户对此类型的变量进行定义,即禁止在栈内存空间内创建此类型的对象。要创建对象,只能用new在堆上进行;第二、禁止用户在程序中使用delete 删除此类型对象。对象的删除只能在类内实现,也就是说只有类的实现者才有可能实现对对象的delete,用户不能随便删除对象。如果用户想删除对象的话,只能按照类的实现者提供的方法进行。
可见,这样做之后大大限制了用户对此类的使用。一般来说不要这样做,通常这样做是用来达到特殊的目的,比如在单例模式设计上,大家可以自行检索资料进行理解。

你可能感兴趣的:(C++)