类别基础的面向对象程序设计中,构造器(英语: Constructor,有时简称 ctor),别称:构造方法构造函数建构子、建构式)是一个里用于建立对象的特殊子程序。它能初始化一个新建的对象,并时常会接受参数用以设定实例英语Instance (computer science)变量

构造器跟一般的实例方法十分相似;但是与其它方法不同,构造器没有返回类型英语Return type,不会被继承,且不会有范围修饰符。构造器的函数名称一般与它所属的的名称相同。 它承担着初始化对象数据成员并建立类不变象的任务;在类不变象无效的时候它会失败。一个正确编写的构造器会使它生成的对象保持在一个有效状态。不可变物件必须在构造器内完成所有初始化。

多数编程语言允许构造器重载 - 一个类被允许拥有多个接受不同参数英语Parameter (computer programming)种类的构造器同时存在。一些编程语言允许某些特殊种类的构造器。使用单个类来具体地建立和返回新实例的构造器,时常被抽象为工厂方法 - 一种同样用来建立新对象,但会同时使用多个类,或者一些诸如对象池的分配方案来完成这一过程的子程序。

种类

参数化构造器

接收参数的构造器被称为参数化构造器。参数的数量可以大于或等于一。例如,在C++中:

class Example
{
     int x, y;
   public:
     Example();
     Example(int a, int b); // Parameterized constructor
};
Example :: Example()
{
}
Example :: Example(int a, int b)
{
     x = a;
     y = b;
}

使用参数化构造器声明对象时,必须传入初始值作为构造器的参数。在这种情况下,一般的声明对象的方法可能不再适用。 构造器函数的调用方式可以分为显式和隐式两种。

    Example e = Example(0, 50); // Explicit call

    Example e(0, 50);           // Implicit call

缺省构造器

如果在编写一个可实例化的类时没有专门编写构造器,多数编程语言会自动生成缺省构造器

缺省构造器的特性依不同语言而定。某些情况下它会将所有的实例变量同时初始化到0,或者任何其他别的值;某些缺省构造器什么也不会做。

某些语言 (Java, C#, VB .NET) 会缺省构造由该类类型定义的数组,使其充满空值引用。没有空值引用的语言一般会禁止缺省构造包含不可缺省构造对象的数组,或者要求在建立时专门初始化这些数值 (C++):

#include <iostream>

class student{
    public:
        int a,b;
        student(a=0,b=0)   //default constructor
};

int main() {
}

转换建构子

转换建构子可定义从使用者定义或内建类型转换成使用者定义类型的作业。[1]

复制建构子

复制建构子(英语:Copy constructor)是C++程式语言中的一种特别的建构子,习惯上用来建立一个全新的物件,这个全新的物件相当于已存在物件的副本。这个建构子只有一个参数(引数):就是用来复制物件的参照(常用const修饰)。建构子也可以有更多的参数,但除了最左第一个参数是该类别的参照类别型外,其它参数必须有预设值。

移动建构子

移动构造函数(英语:Move constructor)是 C++11 中新增的一种构造函数,用来避免多余的分配新内存——复制——销毁旧内存的操作。参见 C++11 条目内的介绍。

语法

  • Java, C++, C#, ActionScript 和 PHP 4 中的命名规范会要求构造器函数的名称与它所在类的名称相同。
  • PHP 5 建议的构造器函数名称为 __construct。为了保证向下兼容,__construct 方法无法找到时会调用任何跟类名同名的方法作为构造器。从 PHP 5.3.3 起,这种途径只对非命名空间的类有效。[2]
  • 在 Perl 里,构造器被约定俗成地命名为 "new",并且会完成建立对象的大量工作。
  • 在 Perl 的 Moose 对象系统中,构造函数(叫做 new)是自动生成的,程序员可以通过指定一个 BUILD 方法来对其进行扩充。
  • Visual Basic .NET 里,构造器被命名为 New,是个 Sub
  • Python 里构造器的被分为 __new____init__ 两个方法。__new__ 方法负责为实例分配内储存空间,并接受自身的类作为参数(一般命名为 cls)。__init__ 方法接受被新建的实例作为参数(一般称为 self[3])。
  • Object Pascal 的构造函数用关键字 constructor 标识,并且可以起任意名字(但一般来说会被起名为 Create)。
  • Objective-C 的构造函数分成两个方法,allocinitalloc 方法分配内存,init 负责初始化。new 方法会调用 allocinit 两者。

内存机制

在 Java, C# 和 VB .NET 里,构造器会在一种叫做堆积的特殊数据结构里建立作为引用类型的实例。数值类型(例如 int, double 等等)则会建立在叫做堆叠的有序数据结构中。VB .NET and C# 会允许用new来建立值类型的实例。然而在这些语言中,即使使用这种方法建立的对象依然只会在栈上。

在 C++ 中,不用 new 建立的对象会保存在栈上,使用 new 建立时则会在堆积上。它们必须分别使用析构函数或者 delete 操作才能被删除。

语言细节

Java

在Java里,构造器和其他方法的主要差别在于:

  • 构造器不具有任何显性返回类型。
  • 构造器无法直接被“new”启动。
  • 构造器无法被标示为synchronized, final, abstract, native, 或者 static

Java 里的构造器会按照以下顺序完成下列工作:

  1. 将类变量初始到缺省值。(byte, short, int, long, float, 和 double 变量会默认设为它们相应的0值,booleans 会被设为 false, chars 会被设为空字符('\u0000'),对象引用会被设为 null)
  2. 引用父类的构造器,如果没有定义任何构造器。
  3. 将实例变量初始化到指定值。
  4. 执行构造器内的代码。

在 Java 中可以通过关键词 super 访问父类的构造器。

public class Example
{
    // Definition of the constructor.
    public Example()
    {
        this(1);
    }

    // Overloading a constructor
    public Example(int input)
    {
        data = input; // This is an assignment
    }

    // Declaration of instance variable(s).
    private int data;
}
// Code somewhere else
// Instantiating an object with the above constructor
Example e = new Example(42);

不接收任何参数的构造器被称作“无参数构造器”。[4]

Visual Basic .NET

Visual Basic .NET中, 建构子以"New"为定义方法,并且必须是个 Sub。

Class Foobar
    Private strData As String

    ' Constructor
    Public Sub New(ByVal someParam As String)
        strData = someParam
    End Sub
End Class
' code somewhere else
' instantiating an object with the above constructor
Dim foo As New Foobar(".NET")

C#

public class MyClass
{
    private int a;
    private string b;

    // Constructor
    public MyClass() : this(42, "string")
    {
    }

    // Overloading a constructor
    public MyClass(int a, string b)
    {
        this.a = a;
        this.b = b;
    }
}
// Code somewhere
// Instantiating an object with the constructor above
MyClass c = new MyClass(42, "string");

C# 静态建构子

C#中,静态建构子是用来初始化任何静态资料。静态构造函数也称为“类构造函数”,由于类构造函数在生成的 MSIL 里名为“.cctor”,因此也被称为“cctor”。[5][6]

静态构造函数允许复杂的静态变量初始化。[7]

静态构造函数在该类第一次被访问时调用,任何使用该类的操作(无论是调用静态函数、属性还是访问静态变量,还是构造类的实例)都会引发静态构造函数的执行。静态构造函数是线程安全的,并且是单例的。当用在泛型类中时,静态构造函数对于泛型的每个实例化都调用一次。静态变量也同样如此。

public class MyClass
{
    private static int _A;

    // Normal constructor
    static MyClass()
    {
        _A = 32;
    }

    // Standard default constructor
    public MyClass()
    {

    }
}
// Code somewhere
// Instantiating an object with the constructor above
// right before the instantiation
// The variable static constructor is executed and _A is 32
MyClass c = new MyClass();

C++

C++ 的构造函数以该类的名称为标识,且不须写返回值类型也无法返回值:

class C{
public:
  C(void){
    ...
  }
};

构造函数的函数体执行是在各个成员构造完之后才开始,因此要想更改成员的构造方式需要使用成员初始化列表:

class B{
public:
  std::string str;
  B(const char *psz): str(psz){
    ...
  }
};

基类的方式与成员相同,是在初始化列表中写基类名、接构造函数参数表:

class D: public B{
public:
  D(void): B("Hello, world!"){
    ...
  }
};

复制构造函数接受同类对象的左值引用(一般为 const T &)、移动构造函数接受右值引用(一般为 T&&):

class E{
public:
  E(const E &e){...}//Copy constructor
  E(E &&e){...}//Move constructor
};

C++ 中,程序员若未对某类定义构造函数(以及赋值函数、析构函数),编译器在满足条件时会定义相应的函数,参见 [8][9][10][11][12][13] 等页面。

F#

Eiffel

CFML

component initmethod="Cheese" {
   // properties
   property name="cheeseName";

   // constructor
   function Cheese Cheese( required string cheeseName ) {
      variables.cheeseName = arguments.cheeseName;
      return this;
   }
}

Object Pascal

program OopProgram;

type
  TPerson = class
  private
    FName: string;
  public
    property Name: string read FName;
    constructor Create(AName: string);
  end;

constructor TPerson.Create(AName: string);
begin
  FName := AName;
end;

var
  Person: TPerson;
begin
  Person := TPerson.Create('Peter'); // allocates an instance of TPerson and then calls TPerson.Create with the parameter AName = 'Peter'
end.

Perl

package Person;
# In Perl constructors are named 'new' by convention.
sub new {
    # Class name is implicitly passed in as 0th argument.
    my $class = shift;

    # Default attribute values, if you have any.
    my %defaults = ( foo => "bar" );

    # Initialize attributes as a combination of default values and arguments passed.
    my $self = { %defaults, @_ };

    # Check for required arguments, class invariant, etc.
    if ( not defined $self->{first_name} ) {
        die "Mandatory attribute missing in Person->new(): first_name";
    }
    if ( not defined $self->{last_name} ) {
        die "Mandatory attribute missing in Person->new(): last_name";
    }
    if ( defined $self->{age} and $self->{age} < 18 ) {
        die "Invalid attribute value in Person->new(): age < 18";
    }

    # Perl makes an object belong to a class by 'bless'.
    bless $self, $class;
    return $self;
}
1;

Perl with Moose

package Person;
# enable Moose-style object construction
use Moose;

# first name ( a string) can only be set at construction time ('ro')
has first_name => (is => 'ro', isa => 'Str', required => 1);
# last name ( a string) can only be set at construction time ('ro')
has last_name  => (is => 'ro', isa => 'Str', required => 1);
# age (Integer) can be modified after construction ('rw'), and is not required
# to be passed to be constructor.  Also creates a 'has_age' method which returns
# true if age has been set
has age        => (is => 'rw', isa => 'Int', predicate => 'has_age');

# Check custom requirements
sub BUILD {
      my $self = shift;
      if ($self->has_age && $self->age < 18) { # no under 18s
           die "No under-18 Persons";
      }
}
1;

PHP

class Person
{
    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

Python

>>> class ExampleClass(object):
...     def __new__(cls, value):
...         print("Creating new instance...")
...         # Call the superclass constructor to create the instance.
...         instance = super(ExampleClass, cls).__new__(cls)
...         return instance
...     def __init__(self, value):
...         print("Initialising instance...")
...         self.payload = value
>>> exampleInstance = ExampleClass(42)
Creating new instance...
Initialising instance...
>>> print(exampleInstance.payload)
42

Ruby

irb(main):001:0> class ExampleClass
irb(main):002:1>   def initialize
irb(main):003:2>     puts "Hello there"
irb(main):004:2>   end
irb(main):005:1> end
=> nil
irb(main):006:0> ExampleClass.new
Hello there
=> #<ExampleClass:0x007fb3f4299118>

注释

参见

参考来源

  1. ^ MSDN 類別和結構 (C++). [2016-05-11]. (原始内容存档于2016-06-16). 
  2. ^ Constructors and Destructors页面存档备份,存于互联网档案馆), from PHP online documentation
  3. ^ Data model页面存档备份,存于互联网档案馆), from Python online documentation
  4. ^ Providing Constructors for Your Classes. Oracle Corporation. 2013 [2013-12-20]. (原始内容存档于2021-12-03). 
  5. ^ Fabulous Adventures in Coding. Eric Lippert. 2013-02-06 [2014-04-05]. (原始内容存档于2017-07-07). 
  6. ^ Expert .NET 2.0 IL Assembler. APress. 2006-01-01 [2014-04-05]. (原始内容存档于2015-04-14). 
  7. ^ Static Constructor in C# on MSDN. [2016-03-30]. (原始内容存档于2016-10-30). 
  8. ^ Default constructors. cppreference. [2016-10-29]. (原始内容存档于2020-12-30). 
  9. ^ Copy constructors. cppreference. [2016-10-29]. (原始内容存档于2020-11-12). 
  10. ^ Move constructors. cppreference. [2016-10-29]. (原始内容存档于2021-03-05). 
  11. ^ Copy assignment operator. cppreference. [2016-10-29]. (原始内容存档于2021-01-16). 
  12. ^ Move assignment operator. cppreference. [2016-10-29]. (原始内容存档于2020-11-12). 
  13. ^ Destructors. cppreference. [2016-10-29]. (原始内容存档于2021-03-02).