2012-05-08 219 views
5

我見過很多代碼,其中編碼器爲類定義了一個init()函數,並在創建實例後稱其爲第一件事。在構造函數中初始化

在構造函數中進行所有初始化是否有任何傷害或限制?

+2

爲什麼用Java和C++標記?商榷? –

+1

不,但有時會調用init在兩個不同的構造函數之後初始化 - 以避免代碼粘貼。 – Anycorn

+0

虛擬init()可以用於多態初始化 – vid

回答

0

它是有例外做一個設計模式從對象構造函數中拋出。

在C++中,如果從對象構造函數中拋出異常,那麼該對象根據語言運行庫被視爲未構造。 因此,當對象超出範圍時,不會調用對象析構函數。

這意味着,如果你有你這樣的構造函數中的代碼:

int *p1 = new int; 
int *p2 = new int; 

和這樣的代碼在你的析構函數:

delete p1; 
delete p2; 

P2初始化在構造函數中失敗由於沒有更多的可用內存,那麼運算符新的運算符拋出bad_alloc異常。 此時,即使已正確分配了p1的內存,您的對象仍未完全構建。 如果發生這種情況,析構函數將不會被調用,並且正在泄漏p1。

因此,您在構造函數中放置的代碼越多,發生錯誤的可能性就越大,從而導致潛在的內存泄漏。

這是設計選擇的主要原因,畢竟這不是太瘋狂。

更多關於這對香草薩特的博客:Constructors exceptions in C++

0

這是一個設計選擇。你想讓你的構造函數儘可能簡單,所以很容易閱讀它正在做什麼。這就是爲什麼你會經常看到構造函數調用其他方法或函數,這取決於語言。它允許程序員閱讀並遵循邏輯,而不會在代碼中迷失方向。

隨着構造函數的推出,您可以快速運行一個場景,您可以觸發大量的事件。良好的設計決定了你將這些序列分解爲更簡單的方法,再次使它更易讀,更容易維護。

所以,不,沒有任何傷害或限制,這是一個設計偏好。如果你需要在構造函數中完成所有的初始化,那麼在那裏完成。如果您以後只需要它,請將其放入稍後調用的方法中。無論哪種方式,這完全取決於你,沒有硬性規定或快速規則。

2

通常對於可維護性,減少代碼大小,當多個構造函數調用相同的初始化代碼:

class stuff 
{ 
public: 
    stuff(int val1) { init(); setVal = val1; } 
    stuff()   { init(); setVal = 0; } 

    void init()  { startZero = 0; } 

protected: 
    int setVal; 
    int startZero; 
}; 
1

在Java中,有充分的理由保持構造短,移動到init()方法初始化邏輯:

  • 構造函數不會被繼承,所以任何子類都必須重新實現它們或提供與super
  • 你不應該調用構造函數重寫的方法,因爲你可以在一個不一致的狀態找到你的目標在那裏部分初始化
+0

你的兩個參數似乎都反對使用'init()'方法。使用'super'清楚地表明你正在初始化基類,'init'可以被覆蓋。事實上'init'可以在派生類中定義是一個非常強烈的反對在基類中使用它的論點。 –

2

正好相反:它通常是更好地把所有的初始化的 在構造函數中。在C++中,「最佳」策略通常是將 初始化設置在初始化程序列表中,以便成員使用正確的值直接構建,而不是構建默認的 ,然後分配。在Java中,您希望避免函數 (除非它是privatefinal),因爲動態分辨率可以將 放入尚未初始化的對象中。

關於你使用init()函數的唯一原因是因爲你的 有很多具有顯着通用性的構造函數。 (在C++的情況下 ,你仍然必須權衡默認 建設,然後分配與使用 正確的值近期建設之間的差額。)

0

如果你有相同的類的多個對象或需要與指針被初始化爲彼此,所以有指針的至少一個週期的不同類別依賴關係,你不能單獨在構造函數中完成所有初始化。 (當另一個對象還沒有被創建時,你將如何構造帶有指向另一個對象的指針/引用的第一個對象?)

這種情況很容易發生的一種情況是在事件仿真系統中,其中不同的組件交互,以便每個組件都需要指向其他組件的指針。

由於無法在構造函數中完成所有初始化,因此至少有一些初始化必須在init函數中發生。這導致了一個問題:在init函數中應該完成哪些部分?靈活的選擇似乎是在初始化函數中完成所有的指針初始化。然後,您可以按照任意順序構造對象,因爲在構建給定對象時,您不必擔心是否已經有必要指向其他需要了解的對象。