2009-07-31 28 views
2

這個問題是從喬爾的靈感「使代碼錯看錯了」我們可以利用類型系統使程序更安全嗎?

http://www.joelonsoftware.com/articles/Wrong.html

有時你可以使用類型來實施對超出其接口的對象語義。例如,Java接口Serializable實際上並沒有定義方法,但實際上一個實現Serializable的對象說明了它應該如何使用。

我們是否可以在Java中使用UnsafeString和SafeString接口/子類,這些接口/子類的使用方式與Joel的匈牙利符號和Java的Serializable大致相同,因此它不會看起來很糟糕 - 它不會編譯?

這是可行的領域是Java/C/C++或者是類型系統過小或過動?

而且,超越輸入清理,還有什麼其他安全功能可以以這種方式實現的?

+1

這也在播客58(http://blog.stackoverflow.com/2009/06/podcast-58/) – Brian 2009-07-31 18:58:58

+0

討論它基本上是一個什麼類型的系統*是*。沒有類型系統,任何變量都可以被視爲任何其他變量。你可以通過將一個int作爲一個double來訪問出界限內存。這可以讓你得到4個字節的值超過int的結尾。或無符號整數可防止您意外存儲已簽名的值。 (儘管他們默默地轉換它的方式並不理想) – jalf 2009-07-31 19:35:01

+0

好吧,但是好像我們可以在類型系統中強制執行更高級別的屬性,這與我們如何操作數據表示形式無關。我想,基本上將類型系統進一步引入業務邏輯。 – 2009-07-31 19:51:08

回答

3

類型系統已經執行大量這樣的安全功能。這實質上就是對於

對於一個非常簡單的示例,它可以防止您將float作爲int處理。這是安全性的一個方面 - 它保證您正在使用的類型將按預期行事。它保證只對字符串調用字符串方法。例如,大會沒有這種保護措施。

這也是類型系統的工作,以確保您不要在一個類上調用私有函數。這是另一個安全功能。

Java的類型系統是太貧血有效地執行了很多有趣的制約,但在許多其他語言(包括C++),類型系統可以用來執行更廣泛的規則。

在C++中,模板元編程爲您提供了很多工具,禁止「壞」的代碼。例如:

class myclass : boost::noncopyable { 
... 
}; 

在編譯時強制該類不能被複制。下面將產生編譯錯誤:

myclass m; 
myclass m2(m); // copy construction isn't allowed 
myclass m3; 
m3 = m; // assignment also not allowed 

同樣,我們可以保證在一個模板函數只被調用上達到一定的標準(比如,他們必須是隨機訪問迭代器類型的編譯時間,而雙線性者是不允許的,或者它們必須是POD類型,或者它們不能是任何類型的整數類型(char,short,int,long),但是所有其他類型都應該合法。

模板元編程的教科書示例在C++中實現了一個用於計算物理單位的庫,它允許您將「meter」類型的值與另一個相同類型的值相乘,並自動確定結果必須是「square meter」類型。類型「英里」的類型爲「小時」類型的值,並獲得「每小時英里數」類型的單位。

同樣,這是一種安全功能,可防止您混淆您的類型並意外地混淆您的單位。如果您計算一個值並嘗試將其分配給錯誤的類型,則會出現編譯錯誤。試圖用meters^2來分割,例如,將結果賦值爲千克等值會導致編譯錯誤。

大部分,這需要一些手動設置工作,當然,但語言給你,你需要基本建成你想要的類型檢查的工具。其中一些可以直接用語言更好地支持,但是更有創意的檢查在任何情況下都必須手動實施。

-1

你不能有字符串的UnsafeString子類在Java中,因爲java.lang.String is final

一般情況下,你不能提供任何形式的源級別的安全性 - 如果你想防止邪惡的代碼,你必須做的二進制級別(例如Java字節碼)。這就是爲什麼private/protected不能用作C++中的安全機制:可以通過指針操作繞過它。

+0

我們無法抵禦邪惡的Java代碼。我們正在防範惡意輸入(例如,針對跨站點腳本)。使用編譯器去除「氣味」而不是Joel的匈牙利方法是這裏的願望。 – 2009-07-31 18:47:12

+0

但是最後的課程部分的呼籲。儘管如此,這只是一個障礙,而不是僵局。 – 2009-07-31 18:48:11

1

是你可以做這樣的事情。我不瞭解Java,但是在C++中這不是習慣,也不支持這個,所以你必須做一些手動工作。在其他語言中習慣使用,例如Ada,它具有等價的typedef,它引入了一個新類型,它不能隱式轉換爲原始類型(這個新類型「繼承」了它的一些基本操作創建,所以它保持有用)。

順便說一句,在一般情況下,繼承並不是引入新類型的好方法,因爲即使沒有一種方式的隱式轉換,另一種方式也有一種。

0

你可以做這樣的certian量出阿達盒子。例如,您可以製作不能互相操作的整數類型,並且Ada枚舉與任何整數類型都不兼容。你仍然可以在它們之間進行轉換,但你必須明確地這樣做,這需要注意你在做什麼。你可以在現在的C++中做同樣的事情,但是你必須把所有的整數和枚舉包裝在類中,這對於那些應該很簡單的東西來說只是太多的工作(或者更好的是,默認的做事的方式)。

我知道下一個版本的C++將會解決至少枚舉問題。

0

在C++中,我想你可以使用typedef創造的代名詞基本類型。您的同義詞可能意味着該變量的內容,取代匈牙利應用程序的功能。

智能感知會報告你申報時使用的代名詞,所以如果你不喜歡使用實際的匈牙利,但它從滾動約(或使用轉到定義)救你。

0

我猜你正在考慮一些與Perl的「污點」分析有關的問題。

在Java中,應該可以使用自定義註釋和註釋處理器來實現這一點。雖然不一定很容易。

相關問題