在类中将构造函数私有化有什么用?


134

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

Answers:


129

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

  1. 只能从类本身内部的静态工厂方法访问构造函数。Singleton也可以属于此类别。
  2. 一个实用程序类,仅包含静态方法。

9
我什至不必为实用程序类创建私有构造函数。
Petesh 2010年

16
@Patesh:这是您的决定。我和其他人宁愿阻止实用程序类的实例化,也不想遗漏一行私有的构造函数。
nanda 2010年

1
在某些编程语言(尤其是Java)中,它还会阻止继承
dfa 2010年

9
@dfa:为防止继承,您必须置于final类级别。由于这个原因,放置私有构造函数几乎没有用。
nanda 2010年

3
@Will:如果您使用反射,则不会。声明构造函数私有是为了防止实例化(禁止反射),但是防止子类化是副作用,而不是意图。对此合适的工具是声明类final。这就是Sun为String类所做的事情,是不应该扩展的合理的API块。
BobMcGee 2010年

96

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

答:您的类实例是在static方法中创建的。static然后将该方法声明为public

class MyClass()
{
private:
  MyClass() { }

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

B.你的课是单身。这意味着程序中最多只能有一个类的实例。

class MyClass()
{
private:
  MyClass() { }

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

C.(仅适用于即将到来的C ++ 0x标准)您有几个构造函数。其中一些被宣布public,另一些被宣布private。为了减小代码大小,公共构造函数会“调用”私有构造函数,这些私有构造函数依次完成所有工作。public因此,您的构造函数称为委托构造函数:

class MyClass
{
public:
  MyClass() : MyClass(2010, 1, 1) { }

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

D.您想限制对象复制(例如,由于使用共享资源):

class MyClass
{
  SharedResource * myResource;

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

E.您的课程是实用程序课程。这意味着,它仅包含static成员。在这种情况下,程序中永远都不能创建对象实例。


3
在大多数情况下,您将希望防止对象复制。因此,您将不会为私有副本构造函数提供实现。
frast

来自google c ++样式指南(另一个不在列表中,但很常见的示例)-> 如果您需要在构造函数中执行“考虑工厂功能[并使构造函数私有]”(方括号中的文字由我编写) )
特雷弗·博伊德·史密斯

12

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

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

泰瑞,这够不一样吗?;)
Emile Cormier 2010年

好答案。为什么不提及反射,因为这是完成用户无法完成的任务的一种途径?
BobMcGee 2010年

@Bob:您必须对此有所启发,因为我主要是C ++人士,而C ++词汇表中并不包含反射。;)
Emile Cormier 2010年

3
没有人会读这本书,但请看这里:我对此已经被否决了两次。没有沮丧或其他任何事情,但是(为了学习)我想知道为什么这很糟糕?迭代器还可以如何构造访问容器数据所需的数据?
Emile Cormier 2010年

2
@EmileCormier,我认为您不公正地被否决了,因为一再被告知学习C ++的人们:“避免朋友声明”。该建议似乎针对没有经验的C ++程序员,否则他们可能会friend在不需要的地方使用它-并且在很多情况下,这是个坏主意。可悲的是,该消息已经收到了太多好评,许多开发人员对语言的了解还不够好,以至于无法理解偶尔使用friend不仅可以接受,而且更可取。您的例子就是这种情况。故意的强耦合不是犯罪,这是设计决定。
evadeflow

10

所有人都被单例卡住了,哇。

其他事情:

  • 阻止人们在堆栈上创建您的课程;创建私有构造函数,并且仅通过工厂方法交出指针。
  • 防止创建类的副本(私有副本构造函数)

8

这对于包含通用代码的构造函数非常有用。私有构造函数可以由其他构造函数使用“ this(...);”来调用 符号。通过在私有的(或受保护的)构造函数中编写通用的初始化代码,您还可以清楚地知道它仅在构造过程中被调用,如果仅仅是一个方法,则不是这样:

public class Point {
   public Point() {
     this(0,0); // call common constructor
   }
   private Point(int x,int y) {
     m_x = x; m_y = y;
   }
};

3

在某些情况下,您可能不想使用公共构造函数。例如,如果您要单身人士课。

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


3

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

一个示例:类上的静态工厂方法可以在工厂方法选择分配对象时返回对象(例如,单例工厂)。


@Skilldrick:一个示例是您可以强制仅将类分配给堆。
德克2010年

@dirk我已删除我的评论,因为它不再相关。
Skilldrick 2010年

3

我们还可以使用私有构造函数,以仅通过特定类来创建对象(出于安全原因)。

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

C ++示例:

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的构造方法。



2

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


1

如果它是私有的,则不能调用它==>,不能实例化该类。在某些情况下很有用,例如单例。

有一个讨论,更多的例子在这里


1

我看到您有一个问题要解决。

简而言之,如果您不想允许其他人创建实例,则将构造器限制在有限范围内。实际应用(一个示例)是单例模式。


1

不应该将构造函数设为私有。期。使其受到保护,因此可以根据需要扩展类。

编辑:无论您投多少票,我都支持。 您正在切断代码将来的开发潜力。如果其他用户或程序员确实确定要扩展该类,则只需将构造函数更改为源代码或字节码中的protected。除了使他们的生活更加艰难之外,您将一事无成。在构造函数的注释中包含警告,然后保留该警告。

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

有效用途:

  • protected 构造函数的一个很好的用途是强制使用静态工厂方法,该方法允许您限制实例化或池化和重用昂贵的资源(数据库连接,本机资源)。
  • 单例(通常不是很好的做法,但有时是必要的)

2
根据我的经验,没有绝对的真理,如果情况需要,甚至可以使用goto。有坏与邪恶的方法,但是正如马歇尔·克莱恩(Marshall Cline)所说,有时您需要在较小的邪恶之间进行选择。 parashift.com/c++-faq-lite/big-picture.html#faq-6.15 至于私有构造函数,它们是必要的,甚至对您也不是一件坏事。这只是意味着没有人,包括您的子类,都应该使用它。
daramarak

Gotos确实有其地位,但是将构造函数标记为私有不会给您带来任何麻烦。我已经编辑了帖子,以更全面地解释原因。
BobMcGee 2010年

复制某些类的构造或默认构造没有意义。在C ++中,您可以通过声明一个私有ctor而不定义它(对于operator =也很常见)来表明这一点。

实际上,优化Java JIT和GWT之类的编译器确实使用了私有构造函数:限制了实例化的范围,并更好地内联/修剪了代码。
阿贾克斯

1

构造函数出于某些目的是私有的,例如当您需要实现单例或限制类的对象数时。例如在单例实现中,我们必须将构造函数设为私有

#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个,请使用以下命令

#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

}

谢谢


1

您可以有多个构造函数。如果您未明确提供C ++,则会提供一个默认构造函数和一个默认副本构造函数。假设您有一个只能使用某些参数化构造函数构造的类。也许是初始化变量。如果用户然后在没有该构造函数的情况下使用此类,则他们将不会引起任何问题。一个很好的通用规则:如果默认实现无效,则将default和copy构造函数设为私有,并且不提供实现:

class C
{
public:
    C(int x);

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

使用编译器可防止用户将对象与无效的默认构造函数一起使用。


2
当您提供非默认构造函数而不指定默认构造函数时,默认构造函数不存在。无需创建它并使其私有。
TT_ 2014年

0

引用Effective Java,您可以拥有一个带有私有构造函数的类,以拥有一个定义常量(作为静态final字段)的实用程序类。

编辑:根据评论,这可能仅适用于Java,但我不知道此构造是否适用于/需要其他OO语言(例如C ++))

一个例子如下:

public class Constants {
    private Contants():

    public static final int ADDRESS_UNIT = 32;
    ...
}

EDIT_1:同样,以下解释适用于Java :(并参考《Effective Java》一书)

如下所示的实用程序类的实例,虽然无害,但没有任何作用,因为它们不是设计成实例化的。

例如,说没有常量类的私有构造函数。如下所示的代码块是有效的,但不能更好地传达Constants类用户的意图

unit = (this.length)/new Constants().ADDRESS_UNIT;

与类似的代码相反

unit = (this.length)/Constants.ADDRESS_UNIT;

另外,我认为私有构造函数可以 更好地传达Constants(例如)类的设计者的意图。

如果没有提供构造函数,Java将提供默认的无参数公共构造函数,并且如果您打算防止实例化,则需要一个私有构造函数。

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


2
但是在C ++中,您不需要为此的类。如果某些内容不依赖于对象内部的数据,则将其编写为自由函数和自由变量。要封装吗?使用名称空间。
daramarak

@daramarak:谢谢,我没有使用C ++的经验。我更新了答案以反映这仅适用于Java
sateesh 2010年

2
如果您的对象只是封装静态变量,为什么要限制实例化?为什么要指定有关构造函数的任何内容?实例化它不会造成任何伤害。似乎您可以得到类似的结果,将类声明为static和final。
BobMcGee 2010年

@BobMcGee,感谢您的评论。这使我能够思考(并参考)更多有关我所发布内容的信息。我已经编辑了答案,为私有构造函数的实用性添加了更多理由。
sateesh 2010年

0

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

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}

1
实例化实用程序类为什么有关系?它所做的就是创建一个没有字段的对象,并占用一些字节的内存。
BobMcGee 2010年

能够实例化它会创建一个模糊的API。如果将类设计为不带状态的实用程序类,并希望保持这种状态。您可以使用公共构造函数对一个类进行子类化。一些实用程序方法的子类是白痴。
gpampara

@BobMcGee:显然,设计一个可以工作的类和设计供其他人使用的类是不同的事情。那些从事API开发工作的人(例如Sun公司的人或Google Collections的人)将杀死任何试图说实用程序类中的私有构造函数毫无用处的人。
nanda 2010年

@gpampara:声明您的班级决赛将防止人们子类化。这就是Sun为String类所做的事情。@Nanda:实用程序类不需要定义的构造函数。如果您不希望它具有可扩展性,请使用“ final”声明它不是可扩展性。
BobMcGee 2010年

1
@BobMcGee:您似乎很喜欢看Sun的例子。然后从Sun检查此实用程序类Collections:docjar.com/html/api/java/util/Collections.java.html。它确实在使用私有构造函数。
nanda 2010年

0

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


0

重要的用途之一是在SingleTon类中

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
   }
};

如果您的类只有静态方法,则它也适用。即没有人需要实例化您的课程


应该注意的是,许多人认为单例是“反模式”,应用它的决定不应轻易做出。常见的替代方法是依赖注入。
Michael Aaron Safyan 2010年

SingleTon不是反模式。但是,过度使用模式(由于缺乏其使用知识)是不好的。应当注意,它是GOF模式之一。
SysAdmin 2010年

0

这确实是一个显而易见的原因:您想要构建一个对象,但是在构造函数中(就接口而言)这样做是不实际的。

这个Factory例子很明显,让我演示一下这个Named Constructor成语。

说我有一个Complex可以表示复数的类。

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

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

我可以更改界面以使其更容易:

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,...和私人构造函数将确保用户正确的约束。


0

除了更知名的用途之外...

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

“私有构造函数,公共静态方法”
“实现的对象,接口的功能”

如果要使用对象实现功能,并且该对象在进行一次性计算(通过方法调用)之外没有用,那么您就有一个Throwaway Object。您可以将对象创建和方法调用封装在静态方法中,以防止出现这种常见的反模式:

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

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

z = A.f(x,y);

调用者永远不需要知道或关心您在内部使用对象,产生更清洁的界面以及防止对象周围产生垃圾或对象的错误使用。

例如,如果您想通过方法foobar和分解计算zork,例如共享状态而不必在函数中传入和传出许多值,则可以按以下方式实现它:

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页中给出,该模式是重构模式的最后一步,结束于:

  1. 用原始方法替换原始方法,该方法创建新类的实例,该实例由原始方法的参数和接收器构造,并调用“计算”​​。

这与这里的其他示例有很大的不同:该类是可实例化的(不同于实用程序类),但是实例是私有的(与工厂方法不同,包括单例等),并且可以存在于堆栈中,因为它们永不转义。

这种模式在自下而上的OOP中非常有用,在自下而上的OOP中,对象用于简化低级别的实现,但不一定在外部公开,并且与经常出现并从高级接口开始的自上而下的OOP形成对比。


0

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

除其他外,用于模式:

Singleton pattern
Builder pattern

私有构造函数在不可变对象中如何有用?不变性是关于防止创建后对对象的更改,而私有构造函数是关于防止创建的
尼尔斯·冯·巴特

0

面对域驱动的设计,使用私有构造函数还可以提高可读性/可维护性。摘自“ Microsoft .NET-企业归档应用程序,第二版”:

var request = new OrderRequest(1234);

引用:“这里有两个问题。首先,在查看代码时,几乎无法猜测正在发生什么。正在创建一个OrderRequest实例,但是为什么和使用哪些数据?什么是1234?这导致了第二个问题:您违反了有界上下文的普遍语言,该语言可能会说像这样:客户可以发出订单请求并被允许指定购买ID。如果是这种情况,这是一种获取新OrderRequest实例的更好方法:“

var request = OrderRequest.CreateForCustomer(1234);

哪里

private OrderRequest() { ... }

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

我并不是为每个类都提倡这样做,但是对于上述DDD场景,我认为防止直接创建新对象是很有意义的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.