簡體   English   中英

為什么構造函數將始終具有與類相同的名稱以及如何隱式調用它們?

[英]Why constructors will always have same name as of class and how they are invoked implicitly?

我想知道為什么構造函數的名稱總是與類名的名稱相同,以及在創建該類的對象時如何隱式調用它。 有誰能解釋一下這種情況下的執行流程?

我想知道為什么構造函數的名稱總是與類名的名稱相同

因為此語法不需要任何新關鍵字。 除此之外,沒有充分的理由。

為了最大限度地減少新關鍵字的數量,我沒有使用這樣的顯式語法:

 class X { constructor(); destructor(); } 

相反,我選擇了一個聲明語法來反映構造函數的使用

 class X { X(); ~X(); 

這可能過於聰明了。 [C ++的設計和演變,3.11.2構造函數表示法]


有誰能解釋一下這種情況下的執行流程?

對象的生命周期可以總結如下:

  1. 分配內存
  2. 調用構造函數
  3. 使用對象
  4. 調用析構函數/終結符
  5. 釋放記憶

在Java中,步驟1始終從堆中分配。 在C#中,類也是從堆中分配的,而結構的內存已經可用(在非捕獲的本地結構的情況下在堆棧中或在它們的父對象/閉包內)。 請注意,了解這些細節通常不是必需的或非常有用 在C ++中,內存分配非常復雜,所以我不會在這里詳細介紹。

第5步取決於內存的分配方式。 方法結束后,堆棧存儲器將自動釋放。 在Java和C#中,垃圾收集器在不再需要它的某個未知時間內隱式釋放堆內存。 在C ++中,通過調用delete在技​​術上釋放堆內存。 在現代C ++中,很少手動調用delete 相反,您應該使用RAII對象,例如std::stringstd::vector<T>std::shared_ptr<T>來自己處理。

為什么? 因為你提到的不同語言的設計師決定以這種方式制作它們。 某人完全有可能設計一種OOP語言,其中構造函數不必與類具有相同的名稱(如注釋,python中就是這種情況)。

這是區分構造函數和其他函數的簡單方法,並使代碼中的類構造非常易讀,因此作為語言設計選擇是有意義的。

這種機制在不同的語言中略有不同,但實質上這只是一種語言功能輔助的方法調用(例如java和c#中的new關鍵字)。

每當創建新對象時,運行時都會調用構造函數。

在我看來,使用sepearte關鍵字來聲明構造函數會“更好”,因為它會刪除對類本身名稱的其他不必要的依賴。

然后,例如,類中的代碼可以作為另一個的主體復制,而不必對構造函數的名稱進行更改。 為什么人們想要這樣做我不知道(可能在一些代碼重構過程中),但重點是人們總是在努力實現事物之間的獨立性,我認為語言語法與此相反。

析構函數也是如此。

構造函數具有相同名稱的一個很好的理由是它們的表現力。 例如,在Java中,您可以創建一個對象,

MyClass obj = new MyClass();  // almost same in other languages too

現在,構造函數定義為,

class MyClass {
  public MyClass () {... }
}

所以上面的語句很好地表達了,你正在創建一個對象,而在這個過程中,構造函數MyClass()被調用。

現在,無論何時創建對象,它總是調用其構造函數。 如果該類extend了其他一些Base類,那么將首先調用它們的構造函數,依此類推。 所有這些操作都是隱含的。 首先分配對象的內存(在堆上)然后調用構造函數來初始化對象。 如果您不提供構造函數,編譯器將為您的類生成一個。

在C ++中,嚴格來說構造函數根本沒有名稱。 在標准狀態下的12.1 / 1,“構造函數沒有名稱”,它沒有比那更清楚。

在C ++中聲明和定義構造函數的語法使用類的名稱。 必須有一些方法,並使用類的名稱簡潔,易於理解。 C#和Java都復制了C ++的語法,大概是因為它至少對一些他們所針對的受眾很熟悉。

精確的執行流程取決於你所說的語言,但你列出的三個共同之處在於,首先從某個地方分配一些內存(可能是動態分配的,也許是堆棧內存的某個特定區域或者其他)。 然后,運行時負責確保以正確的順序調用正確的構造函數或構造函數,用於最派生的類以及基類。 由實現決定如何確保這種情況發生,但所需的效果由每種語言定義。

對於C ++中最簡單的情況,對於沒有基類的類,編譯器只是調用由創建對象的代碼指定的構造函數,即與提供的任何參數匹配的構造函數。 一旦你有幾個虛擬基地,它會變得更加復雜。

我想知道為什么構造函數的名稱總是與類名的名稱相同

這樣它就可以毫不含糊地被識別為構造函數。

以及當我們創建該類的對象時如何隱式調用它。

它由編譯器調用,因為它已經被明確地識別,因為它的命名符號。

有誰能解釋一下這種情況下的執行流程?

  1. 調用新的X()運算符。
  2. 分配內存,或拋出異常。
  3. 調用構造函數。
  4. new()運算符返回給調用者。

問題是為什么設計師決定如此?

在類之后命名構造函數是一個歷史悠久的約定,至少可以追溯到20世紀80年代早期的C ++早期,可能是它的Simula前身。

構造函數與該類的名稱相同的約定用於編程簡易性,構造函數鏈接以及語言的一致性。

例如,考慮一個您想要使用Scanner類的場景,現在如果JAVA開發人員將構造函數命名為xyz,該怎么辦!

那你怎么知道你需要寫:

掃描儀scObj = new xyz(System.in);

這可能真的很奇怪,對! 或者,您可能必須引用一個巨大的手冊來檢查每個類的構造函數名稱以便創建對象,如果您只需命名構造函數與類的構造函數相同就可以解決問題,那么這也是毫無意義的。

其次,構造函數本身是由編譯器創建的,如果你沒有顯式提供它,那么構造函數的最佳名稱可以由編譯器自動選擇,所以程序員很清楚! 顯然,最好的選擇是保持與班級相同。

第三,您可能聽說過構造函數鏈接,然后在構造函數中鏈接調用時,編譯器將如何知道您為鏈接類的構造函數指定了什么名稱! 顯然,問題的解決方案也是一樣的,保持構造者的名稱與類的一樣。


在創建對象時,通過使用new關鍵字在代碼中調用它來調用構造函數(並在需要時傳遞參數),然后通過鏈接最終給出對象的調用來調用所有超類構造函數。

謝謝你的要求。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM