4

我正在維護一個插件(作爲一個DLL實現)爲一個大的封閉的源應用程序。多年來這一直很好。但是,隨着SDK的最新更新,供應商重載了全球運營商的新功能和刪除功能。這給我造成很多麻煩。會發生什麼是我的插件分配一個字符串。我把這個字符串傳遞給一個靜態鏈接的庫來修改它(改變它的長度,從而重新分配它)。我的應用程序崩潰。一個庫強制全局重載new/delete對我有用!

原因當然是,該字符串位於供應商分配的自定義堆上。靜態鏈接庫對此堆一無所知,並試圖在該內存上使用默認的新/刪除操作符。繁榮。

現在的問題是:如何保持我的代碼清潔並避免使用供應商的操作員?沒有有條件的預處理器宏。我無法避免包含有問題的頭文件,因爲它包含2000行以上的插件所需的代碼。我無法將提供的分配器傳遞到其他庫中,因爲它沒有提供任何機制。我已經竊聽了供應商。我不知道我還能嘗試什麼?

附錄:經過一番激烈辯論後,我設法說服供應商再次從下一個版本的SDK中刪除重載。我已經通過簡單地破解當前的SDK並手動刪除重載來解決了我的即時問題。感謝此主題中的所有建議。他們作爲論點,進一步「證明」首先爲什麼超載是一個壞主意。

+0

您是否必須避免更改靜態庫? – 2010-01-05 16:46:24

+0

我可以在這種情況下更改它,因爲它是開源的。但是我真的很想避免每次發佈新版本時都需要上傳我的修改。我也更喜歡一個解決方案,與庫工作,我沒有源訪問... – BuschnicK 2010-01-05 16:51:32

+0

此外,你在談論std :: string,char * s或其他類型的字符串? – 2010-01-05 16:56:31

回答

4

如果您正在編譯(通過標頭包含)重寫的新/刪除操作符,則您的代碼中所有調用新/刪除的所有調用都將使用它們。沒有辦法重新覆蓋它(鏈接錯誤),或者只是部分覆蓋它,等等。

重寫全局新/刪除操作符是一種糟糕的形式。這是一個壞主意。如果你不知道爲什麼這是一個壞主意,你沒有資格這樣做。如果你明白爲什麼這是一個壞主意,你有資格這樣做,但你通常會選擇不這樣做。

定義全局新建/刪除在希望人們直接包含到他們的項目中的組件中指數地更爲邪惡。作爲客戶的工作是幫助供應商瞭解情況的嚴重性,或停止成爲客戶。

你可以定義一個自定義的分配器類型(關於如何這樣做,需要接口等,請參閱這個link for a good tutorial),並專門用於STL類型(它是一個模板參數)。

對於shared_ptr,您需要做一些有點不同的事情:如果您不希望使用默認的「delete p」行爲,則需要使用deleter對象作爲構造函數的參數。這不是一個自定義分配器;它只是一個普通的一元函子。

+1

對於被覆蓋的新建和刪除操作而言,您是正確的。 但我不同意你的說法,即覆蓋全局新建和刪除是不好的形式。我已經完成了它,並且在其他人已經完成的幾十個項目上工作。作爲一般的經驗法則,儘管您從不重寫庫中的新建和刪除操作,因爲這可能會讓用戶感到痛苦。這就是爲什麼許多優秀的庫遵循STL約定,允許用戶爲對象指定自己的分配器。但是重寫新的和刪除對於管理系統資源非常方便,你只需要知道你在做什麼。 – Beanz 2010-01-05 23:18:20

0

一種選擇是創建您自己的重載新操作符,可以用malloc來實現。

這可以像定義:

 
enum MyNew {EMyNew}; 

void *operator new(size_t size, MyNew); 

然後,這可以由你稱爲MyClass* myClass = new (EMyNew)MyClass;

因爲這是malloc的方面來實現,它應該像預期。唯一的缺點是,你將不得不取代你使用新的地方的所有實例。

+0

不幸的是,這不是一個選項,因爲我不知道或控制「我已經使用新的所有實例」。考慮STL,鏈接庫等。 – BuschnicK 2010-01-05 16:35:42

+0

STL可以通過使用自定義分配器來克服。
鏈接庫應該是安全的,因爲它們不是針對頭文件進行編譯的。 – doron 2010-01-05 17:01:45

1

您可以使用另一種新的命名空間:

namespace MyNS { 
    // Declare your new/delete operators here 
    // and also declare a class implementing the same interface as std::allocator 
    // using your newly created memory management functions. 
    // Don't forget to put all your classes in the namespace. 
    // (if you don't have one already) 
} 

你就可以使用所有的STL類,給他們你的分配器類型爲模板參數。

+0

將新/刪除放在自己的名稱空間中並不能確保它們始終處於使用狀態(該名稱空間中的類型通常在其他名稱空間內引用)。實際上,它可能會導致更多的分配器不匹配問題。定義一個用於STL類型的新分配器是一個正交的問題,這種類型與全局新/刪除無關,並且不必位於任何特定的名稱空間中。 – 2010-01-05 19:22:28

+0

而且,自定義分配器不是從std :: allocator派生的。請參閱我答案中的鏈接。 – 2010-01-05 19:23:08

+0

更正了有關繼承的錯誤。儘管如此,如果在命名空間中聲明新/刪除操作符,那麼該命名空間中的所有內容都將使用它們。當然,這並不完美(正如你所說,其他不在該名字空間的類不會使用它們)。更好的是恢復原來的全球新的。重載時的一個好主意是使用與標準「nothrow」new相同的重載技巧。 – 2010-01-06 14:16:48

2

難道就沒有可能做到這一點:

namespace evil{ 

#include "evil_header.h" 

} 

那麼什麼evil_header宣佈全球新/刪除變得邪惡::新/邪惡::刪除。如果在evil_header中聲明瞭事物的非頭部定義,我懷疑這會很好地發揮作用。