類別基礎的面向對象程序設計中,構造器(英語: 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).