類別基礎的物件導向程式設計中,建構子(英語: 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裡,建構子和其他方法的主要差別在於:

  • 建構子不具有任何顯性回傳型別。
  • 建構子無法直接被「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")
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++ 的建構式以該類別的名稱為標識,且不須寫回傳值型別也無法回傳數值:

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] 等頁面。

Eiffel

編輯
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.
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;
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
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).