9

我已经看到了构造函数被声明为私有而析构函数是公共的代码。这样的声明有什么用?析构函数是否需要公开,以便在继承期间可以调用,还是代码中的错误?

这个问题可能看起来有点缺乏信息,但我真正想知道的是,当构造函数被要求为私有时,是否有一个公共析构函数遵守 C++ 规则?

4

6 回答 6

9

简答

将构造函数创建为私有但将析构函数创建为公共有许多实际用途。

您可以使用此范例:

长答案

上面我暗示您可以使用私有构造函数和析构函数来实现几种设计模式。嗯,这里是如何...

引用计数

在对象中使用私有析构函数有助于引用计数系统。这使开发人员可以更好地控制对象的生命周期。

class MyReferenceObject
{
public:
    static MyReferenceObject* Create()
    {
        return new MyReferenceObject();
    }

    void retain()
    {
        m_ref_count++;
    }

    void release()
    {
        m_ref_count--;
        if (m_ref_count <= 0)
        {
            // Perform any resource/sub object cleanup.
            // Delete myself.
            delete this; // Dangerous example but demonstrates the principle.
        }
    }
private:

    int m_ref_count;

    MyReferenceObject()
    {
        m_ref_count = 1;
    }

    ~MyReferenceObject() { }

}

int main()
{
    new MyReferenceObject(); // Illegal.
    MyReferenceObject object; // Illegal, cannot be made on stack as destructor is private.

    MyReferenceObject* object = MyReferenceObject::Create(); // Creates a new instance of 'MyReferenceObject' with reference count.
    object->retain(); // Reference count of 2.
    object->release(); // Reference count of 1.
    object->release(); // Reference count of 0, object deletes itself from the heap.
}

这演示了对象如何管理自身并防止开发人员破坏内存系统。请注意,这是一个危险的示例,因为MyReferenceObject删除自身,请参阅此处了解执行此操作时要考虑的事项列表。

辛格尔顿

单例类中的私有构造函数和析构函数的一个主要优点是强制用户仅以设计代码的方式使用它。无法创建流氓单例对象(因为它是在编译时强制执行的)并且用户无法删除单例实例(同样,在编译时强制执行)。

例如:

class MySingleton
{
public:
     MySingleton* Instance()
     {
        static MySingleton* instance = NULL;
        if (!instance)
        {
            instance = new MySingleton();
        }

        return instance;
     }
private:
    MySingleton() { }
    ~MySingleton() { } 
}

int main()
{
     new MySingleton(); // Illegal
     delete MySingleton::Instance(); // Illegal.
}

看看代码被滥用几乎是不可能的。正确使用MySingleton是在编译时强制执行的,从而确保开发人员必须MySingleton按预期使用。

工厂

在工厂设计模式中使用私有构造函数是强制仅使用工厂来创建对象的重要机制。

例如:

class MyFactoryObject
{
public:

protected:
    friend class MyFactory; // Allows the object factory to create instances of MyFactoryObject

    MyFactoryObject() {} // Can only be created by itself or a friend class (MyFactory).
}

class MyFactory
{
public:
    static MyFactoryObject* MakeObject()
    {

        // You can perform any MyFactoryObject specific initialisation here and it will carry through to wherever the factory method is invoked.
        return new MyFactoryObject();
    }
}

int main()
{
    new MyFactoryObject(); // Illegal.
    MyFactory::MakeObject(); // Legal, enforces the developer to make MyFactoryObject only through MyFactory.
}

这很强大,因为它隐藏了MyFactoryObject开发人员的创建。您可以使用工厂方法来执行任何初始化MyFactoryObject(例如:设置 GUID、注册到 DB)以及使用工厂方法的任何地方,初始化代码也将发生。

概括

这只是说明如何使用私有构造函数和析构函数来强制正确使用 API 的几个示例。如果你想变得棘手,你也可以结合所有这些设计模式;)

于 2013-08-31T08:43:43.920 回答
4

第一件事:析构函数可以是私有的。

当构造函数必须是私有的遵守 C++ 规则时,有一个公共析构函数吗?

它完全在 C++ 中工作。事实上,这种场景的一个很好的例子是单例模式,其中构造函数是私有的,而析构函数是公共的。

于 2013-08-31T08:31:09.463 回答
2

以相反的顺序。

析构函数是否需要公开,以便在继承期间可以调用,还是代码中的错误?

实际上,要使继承起作用,析构函数至少应该是protected. 如果从带有private析构函数的类继承,则派生类无法生成析构函数,这实际上阻止了实例化(您仍然可以使用static方法和属性)。

这样的声明有什么用?

请注意,即使构造函数是private,但没有进一步说明该类具有(默认生成的)公共复制构造函数和复制赋值运算符。这种模式经常发生在:

  • 命名构造函数习语
  • 一个工厂

命名构造函数的例子:

class Angle {
public:
    static Angle FromDegrees(double d);
    static Angle FromRadian(double d);

private:
    Angle(double x): _value(x) {}
    double _value;
};

因为是否应该以度数或弧度(或其他)精确是模棱两可的,所以构造了构造函数并提供了命名方法。这样,用法使单位显而易见:xprivate

Angle a = Angle::FromDegrees(360);
于 2013-08-31T13:43:47.947 回答
1

如果您想防止创建多个类的实例,则将构造函数设为私有。这样你就可以控制实例的创建而不是它们的破坏。因此,析构函数可能是公共的。

于 2013-08-31T08:28:57.973 回答
0

我脑海中的一个例子,假设你想将类实例数限制为 0 或 1。例如,对于一些单例类,你希望应用程序可以临时销毁对象以减少内存使用。实现此构造函数将是私有的,但析构函数将是公共的。请参阅以下代码片段。

class SingletoneBigMemoryConsumer
{
private:
    SingletoneBigMemoryConsumer()
    {
        // Allocate a lot of resource here.
    }

public:
    static SingletoneBigMemoryConsumer* getInstance()
    { 
        if (instance != NULL) 
            return instance;
        else
            return new SingletoneBigMemoryConsumer();
    }
    ~SingletoneBigMemoryConsumer()
    {
        // release the allocated resource.
        instance = NULL;
    }
private:
    // data memeber.
    static SingletoneBigMemoryConsumer* instance;
}



//Usage.
SingletoneBigMemoryConsumer* obj = SingletoneBigMemoryConsumer::getInstance();
// You cannot create more SingletoneBigMemoryConsumer here.
// After 1 seconds usage, delete it to reduce memory usage.
delete obj;
// You can create an new one when needed later
于 2013-08-31T08:53:08.520 回答
0

对象的所有者需要访问析构函数才能销毁它。如果构造函数是私有的,则必须有一些可访问的函数来创建对象。如果该函数将构造对象的所有权转移给调用者(例如返回指向自由存储中对象的指针),则调用者在决定删除对象时必须有权访问析构函数。

于 2013-08-31T11:38:51.447 回答