关于oop:在类中创建私有构造函数有什么用?


What is the use of making constructor private in a class?

为什么我们要在类中使构造函数私有化?因为我们总是需要将构造函数公开。


您可能需要私有构造函数的一些原因:

  • 只能从类本身内部的静态工厂方法访问构造函数。singleton也可以属于这个类别。
  • 只包含静态方法的实用程序类。

  • 通过提供一个私有的构造函数,可以防止类实例在这个类之外的任何地方被创建。提供这种构造函数有几个用例。

    a.类实例是在static方法中创建的。然后将static方法声明为public

    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass()
    {
    private:
      MyClass() { }

    public:
      static MyClass * CreateInstance() { return new MyClass(); }
    };

    B.你的班是单人的。这意味着程序中不存在类的多个实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MyClass()
    {
    private:
      MyClass() { }

    public:
      MyClass & Instance()
      {
        static MyClass * aGlobalInst = new MyClass();
        return *aGlobalInst;
      }
    };

    C.(仅适用于即将到来的C++ 0x标准),您有几个构造函数。其中一部分被宣布为public,另一部分被宣布为private。为了减少代码大小,公共构造函数"调用"私有构造函数,而私有构造函数反过来又执行所有工作。因此,您的public构造函数称为委托构造函数:

    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass
    {
    public:
      MyClass() : MyClass(2010, 1, 1) { }

    private:
      MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
    };

    d.您希望限制对象复制(例如,因为使用共享资源):

    1
    2
    3
    4
    5
    6
    7
    class MyClass
    {
      SharedResource * myResource;

    private:
      MyClass(const MyClass & theOriginal) { }
    };

    你的课是实用课。也就是说,它只包含static个成员。在这种情况下,程序中不能创建任何对象实例。


    离开"后门",允许另一个朋友类/函数以用户禁止的方式构造对象。想到的一个例子是构建迭代器(C++)的容器:

    1
    2
    3
    Iterator Container::begin() { return Iterator(this->beginPtr_); }
    // Iterator(pointer_type p) constructor is private,
    //     and Container is a friend of Iterator.


    每个人都被困在单身的事情上,哇。

    其他事项:

    • 阻止人们在堆栈上创建类;创建私有的构造函数,并且只通过工厂方法返回指针。
    • 阻止创建类的副本(私有副本构造函数)


    这对于包含公共代码的构造函数非常有用;其他构造函数可以使用"this(…);"表示法调用私有构造函数。通过在私有(或受保护)构造函数中生成公共初始化代码,您还可以明确地表明,它只在构造期间调用,如果它只是一个方法,则不是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    public class Point {
       public Point() {
         this(0,0); // call common constructor
       }
       private Point(int x,int y) {
         m_x = x; m_y = y;
       }
    };

    有些情况下,您可能不想使用公共构造函数;例如,如果您想使用单例类。

    如果您正在编写第三方使用的程序集,则可能有许多内部类,您只希望由程序集创建,而不是由程序集的用户实例化。


    这样可以确保(具有私有构造函数的类)控制如何调用构造函数。

    例如:类上的静态工厂方法可以返回对象,因为工厂方法选择分配对象(例如,单例工厂)。


    我们也可以有私有的构造函数,强制特定类创建对象仅限(出于安全原因)。

    一种方法是通过一个朋友班。

    C++示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class ClientClass;
    class SecureClass
    {
      private:
        SecureClass();   // Constructor is private.
        friend class ClientClass;  // All methods in
                                   //ClientClass have access to private
                                   // &   protected methods of SecureClass.
    };

    class ClientClass
    {
    public:
        ClientClass();
        SecureClass* CreateSecureClass()
         {
               return (new SecureClass());  // we can access
                                            // constructor of
                                            // SecureClass as
                                            // ClientClass is friend
                                            // of SecureClass.
         }
    };

    注意:注意:只有clientClass(因为它是SecureClass的朋友)无法调用SecureClass的构造函数。


    下面是私有构造函数的一些用法:

  • 单例模式
  • 限制创建实例的次数
  • 为使用静态工厂方法创建对象提供有意义的名称
  • 静态实用程序类或常量类
  • 防止子类化
  • 构建器设计模式,从而创建不可变类

  • 如果它是私有的,那么您不能调用它=>您不能实例化类。在某些情况下很有用,比如单件。

    这里有一个讨论和更多的例子。


    如果不希望用户创建此类的实例或创建继承此类的类,如java.lang.math,则此包中的所有函数都是static,可以在不创建math实例的情况下调用所有函数,因此将构造函数声明为静态的。


    构造器对于某些目的是私有的,例如需要实现单例或限制类的对象数。例如,在单例实现中,我们必须使构造函数私有

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include<iostream>
    using namespace std;
    class singletonClass
    {


        static int i;
        static singletonClass* instance;
    public:


        static singletonClass* createInstance()
        {


            if(i==0)
            {

                instance =new singletonClass;
                i=1;

            }

            return instance;

        }
        void test()
        {

            cout<<"successfully created instance";
        }
    };

    int singletonClass::i=0;
    singletonClass* singletonClass::instance=NULL;
    int main()
    {


        singletonClass *temp=singletonClass::createInstance();//////return instance!!!
        temp->test();
    }

    同样,如果要将对象创建限制为10,请使用以下命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include<iostream>
    using namespace std;
    class singletonClass
    {


        static int i;
        static singletonClass* instance;
    public:


        static singletonClass* createInstance()
        {


            if(i<10)
            {

                instance =new singletonClass;
                i++;
                cout<<"created";

            }

            return instance;

        }
    };

    int singletonClass::i=0;
    singletonClass* singletonClass::instance=NULL;
    int main()
    {


        singletonClass *temp=singletonClass::createInstance();//return an instance
        singletonClass *temp1=singletonClass::createInstance();///return another instance

    }

    谢谢


    可以有多个构造函数。C++提供默认构造函数和缺省复制构造函数,如果不显式提供构造函数。假设您有一个类,它只能使用一些参数化的构造函数来构造。也许它初始化了变量。如果用户在没有该构造函数的情况下使用这个类,那么它们将不会导致任何问题的结束。一个好的一般规则:如果默认实现无效,则将默认和复制构造函数都设为私有,并且不提供实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class C
    {
    public:
        C(int x);

    private:
        C();
        C(const C &);
    };

    使用编译器可以防止用户使用带有无效默认构造函数的对象。


    我看到你提出的一个问题也在解决同一个问题。

    简单地说,如果您不想让其他人创建实例,那么将构造器保持在一个有限的范围内。实际应用(示例)是单例模式。


    您不应该将构造函数设置为私有的。时期。使其受保护,以便在需要时扩展类。

    编辑:不管你投多少反对票,我都支持你的观点。您切断了代码未来开发的潜力。如果其他用户或程序员真的决定扩展类,那么他们只需将构造函数更改为受源代码或字节码保护的。除了让他们的生活更艰难之外,你什么也没做。在构造函数的注释中包含一个警告,并将其保留。

    如果它是一个实用程序类,那么更简单、更正确、更优雅的解决方案是将整个类标记为"静态final",以防止扩展。仅仅将构造函数标记为私有并没有任何好处;真正确定的用户可能总是使用反射来获取构造函数。

    有效用途:

    • 一个很好的使用EDOCX1[0]构造函数将强制使用静态工厂方法,允许您限制实例化或池的重用昂贵的资源(数据库连接,本地资源)。
    • 单件(通常不是很好的练习,但有时是必要的)


    除了众所周知的用途…

    要实现方法对象模式,我将总结为:

    "Private constructor, public static method"
    "Object for implementation, function for interface"

    如果您想使用对象实现一个函数,而该对象在执行一次性计算(通过方法调用)之外没有用处,那么您就有了一个一次性对象。您可以用静态方法封装对象创建和方法调用,以防止这种常见的反模式:

    1
    z = new A(x,y).call();

    …将其替换为(命名空间)函数调用:

    1
    z = A.f(x,y);

    调用方永远不需要知道或关心您在内部使用对象,生成更干净的接口,并防止对象周围出现垃圾或对象的错误使用。

    例如,如果要在方法foobarzork之间分解计算,例如共享状态而不必在函数中或函数外传递许多值,可以按如下方式实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {
      public static Z f(x, y) {
        A a = new A(x, y);
        a.foo();
        a.bar();
        return a.zork();
      }

      private A(X x, Y y) { /* ... */ };
    }

    此方法对象模式在Smalltalk最佳实践模式中给出,Kent Beck,第34-37页,其中是重构模式的最后一步,结束于:

  • Replace the original method with one that creates an instance of the new class, constructed with the parameters and receiver of the original method, and invokes"compute".
  • 这与这里的其他示例有很大的不同:类是可实例化的(与实用程序类不同),但是实例是私有的(与工厂方法不同,包括单例等),并且可以在堆栈上生存,因为它们永远不会逸出。

    这种模式在自下而上的OOP中非常有用,在这种OOP中,对象用于简化低级实现,但不一定暴露在外部,与通常呈现并以高级接口开头的自上而下的OOP形成对比。


    在域驱动的设计中,私有构造函数的使用还可以提高可读性/可维护性。来自"Microsoft.NET-企业级存档应用程序,第2版":

    1
    var request = new OrderRequest(1234);

    引述,"这里有两个问题。首先,当查看代码时,很难猜出发生了什么。在。正在创建orderrequest的一个实例,但为什么要创建并使用哪些数据?1234是多少?这个导致第二个问题:您违反了有界上下文中无处不在的语言。这个语言可能会这样说:客户可以发出订单请求,并允许指定采购ID。如果是这种情况,下面是获取新订单请求实例的更好方法:"

    1
    var request = OrderRequest.CreateForCustomer(1234);

    哪里

    1
    2
    3
    4
    5
    6
    7
    8
    private OrderRequest() { ... }

    public OrderRequest CreateForCustomer (int customerId)
    {
        var request = new OrderRequest();
        ...
        return request;
    }

    我不是在为每一个类都提倡这样做,但是对于上面的DDD场景,我认为阻止直接创建一个新对象是完全有意义的。


    其中一个重要用途是在singleton类中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Person
    {
       private Person()
       {
          //Its private, Hense cannot be Instantiated
       }

       public static Person GetInstance()
       {
           //return new instance of Person
           // In here I will be able to access private constructor
       }
    };

    如果类只有静态方法,那么它也适用。也就是说没有人需要实例化你的类


    引用有效的Java,可以使用一个具有私有构造函数的类来定义定义常量的实用工具类(作为静态最终字段)。

    (编辑:根据评论,这是一个仅适用于Java的东西,我不知道这个构造是否适用于其他OO语言(如C++)。

    示例如下:

    ZZU1〔2〕

    编辑1:同样,下面的解释在Java中是适用的:(参考书,有效的Java)

    像下面这样的实用程序类的实例虽然不有害,但不起作用任何目的,因为它们不是为实例化而设计的。

    例如,假设没有类常量的私有构造函数。下面这样的代码块是有效的,但不能更好地表达常量类的用户

    ZZU1〔3〕

    与类似代码相比

    ZZU1〔4〕

    另外,我认为一个私有的构造函数传达了常量设计者的意图。(说)上课更好。

    如果没有构造函数,Java提供默认的无参数公共构造函数。如果您的目的是防止实例化,那么私有构造函数是需要。

    不能将顶级类标记为静态,甚至可以实例化最终类。


    如果要控制对象实例的创建方式和时间(以及创建的实例数),有时会很有用。

    除其他外,用于模式:

    1
    2
    Singleton pattern
    Builder pattern


    您可能希望防止类被自由地实例化。以单例设计模式为例。为了保证唯一性,您不能让任何人创建它的实例:-)


    这确实是一个显而易见的原因:您希望构建一个对象,但在构造函数中(从接口的角度)进行此操作是不实际的。

    Factory的例子很明显,让我来演示一下Named Constructor的习惯用法。

    假设我有一个类Complex,它可以表示一个复数。

    1
    class Complex { public: Complex(double,double); .... };

    问题是:构造器期望的是实部和虚部,还是期望的是范数和角度(极坐标)?

    我可以更改界面以使其更简单:

    1
    2
    3
    4
    5
    6
    7
    8
    class Complex
    {
    public:
      static Complex Regular(double, double = 0.0f);
      static Complex Polar(double, double = 0.0f);
    private:
      Complex(double, double);
    }; // class Complex

    这被称为Named Constructor习惯用法:类只能通过显式地声明希望使用哪个构造函数来从头构建。

    这是许多施工方法的特例。设计模式提供了许多构建对象的方法:BuilderFactoryAbstract Factory…。私有构造函数将确保用户受到适当的约束。


    实用程序类可以有私有构造函数。类的用户不应该能够实例化这些类:

    1
    2
    3
    4
    5
    6
    7
    public final class UtilityClass {
        private UtilityClass() {}

        public static utilityMethod1() {
            ...
        }
    }