[英]Avoiding memory leaks while mutating c-strings
出於教育目的,我在一些測試程序中使用cstrings。 我想用占位符縮短字符串,例如“......”。
也就是說,如果我的最大長度設置為13, "Quite a long string"
將變成"Quite a lo..."
。此外,我不想破壞原始字符串 - 因此縮短的字符串必須是副本。
下面的(靜態)方法是我提出的。 我的問題是: 為類縮短的字符串分配內存的類是否也應該負責釋放它? 我現在所做的是將返回的字符串存儲在單獨的“用戶類”中,並將內存釋放到該用戶類。
const char* TextHelper::shortenWithPlaceholder(const char* text, size_t newSize) {
char* shortened = new char[newSize+1];
if (newSize <= 3) {
strncpy_s(shortened, newSize+1, ".", newSize);
}
else {
strncpy_s(shortened, newSize+1, text, newSize-3);
strncat_s(shortened, newSize+1, "...", 3);
}
return shortened;
}
像這樣的函數的標准方法是讓用戶傳入char []緩沖區。 您可以在sprintf()
等函數中看到這一點,它將目標緩沖區作為參數。 這允許調用者負責分配和釋放內存,將整個內存管理問題保存在一個地方。
為了避免緩沖區溢出和內存泄漏,在這種情況下,應始終使用C ++類,例如std::string
。
只有最后一個實例才能將類轉換為低級別,例如char*
。 這將使您的代碼簡單而安全。 只需將您的代碼更改為:
std::string TextHelper::shortenWithPlaceholder(const std::string& text,
size_t newSize) {
return text.substr(0, newSize-3) + "...";
}
在C上下文中使用該函數時,只需使用cstr()
方法:
some_c_function(shortenWithPlaceholder("abcde", 4).c_str());
就這樣!
通常,您不應該像在C中編程一樣使用C ++編程。將C ++視為一種非常不同的語言更為合適。
我從來沒有樂意返回指向本地分配內存的指針。 我喜歡對任何在清理方面稱呼我職能的人保持健康的不信任。
相反,你是否考慮過接受一個緩沖區,你要復制縮短的字符串?
例如。
const char* TextHelper::shortenWithPlaceholder(const char* text,
size_t textSize,
char* short_text,
size_t shortSize)
其中short_text =緩沖區以復制縮短的字符串, shortSize =緩沖區的大小。 您還可以繼續返回指向short_text的const char*
,以方便調用者(如果shortSize不夠大,則返回NULL)。
實際上你應該只使用std::string
,但如果必須,請查看現有的庫以獲取使用指南。
在C標准庫中,最接近您正在執行的操作的函數是
char * strncpy ( char * destination, const char * source, size_t num );
所以我會這樣做:
const char* TextHelper::shortenWithPlaceholder(
char * destination,
const char * source,
size_t newSize);
調用者負責內存管理 - 這允許調用者使用堆棧,堆,或內存映射文件,或任何來源來保存該數據。 您不需要記錄您使用new[]
來分配內存,並且調用者不需要知道使用delete[]
而不是free
或delete
,甚至是更低級別的操作系統調用。 將內存管理留給調用者只是更靈活,更不容易出錯。
返回指向目標的指針只是一個很好的例子,允許你做這樣的事情:
char buffer[13];
printf("%s", TextHelper::shortenWithPlaceholder(buffer, source, 12));
最靈活的方法是返回一個包裝已分配內存的輔助對象,以便調用者不必擔心它。 該類存儲指向內存的指針,並具有復制構造函數,賦值運算符和析構函數。
class string_wrapper
{
char *p;
public:
string_wrapper(char *_p) : p(_p) { }
~string_wrapper() { delete[] p; }
const char *c_str() { return p; }
// also copy ctor, assignment
};
// function declaration
string_wrapper TextHelper::shortenWithPlaceholder(const char* text, size_t newSize)
{
// allocate string buffer 'p' somehow...
return string_wrapper(p);
}
// caller
string_wrapper shortened = TextHelper::shortenWithPlaceholder("Something too long", 5);
std::cout << shortened.c_str();
大多數真正的程序都使用std::string
來實現此目的。
在您的示例中,調用者別無選擇,只能負責釋放已分配的內存。
然而,這是一個易於使用的習慣用語,我不建議使用它。
允許您使用幾乎相同的代碼的一種替代方法是將shortened
更改為引用的計數指針,並讓方法返回引用的計數指針而不是裸指針。
編輯:不,我錯了。 我誤解了你想要做的事情。 調用者必須刪除實例中的內存。
C ++標准規定刪除0 / NULL什么都不做(換句話說,這樣做是安全的),所以你可以刪除它,無論你是否曾經調用過這個函數。 編輯:我不知道這是怎么被遺漏的......你的另一個選擇是放置刪除。 在這種情況下,即使它是不好的形式,你也應該使用placement new來保持分配/釋放在同一個地方(否則不一致會使調試變得荒謬)。
那說,你是如何使用代碼的? 我不知道你何時會多次調用它,但如果你這樣做,如果你不記得每個不同的內存塊,就會有潛在的內存泄漏(我認為)。
我只想使用std::auto_ptr
或Boost::shared_ptr
。 它在出路時自行刪除,可以與char *一起使用。
考慮到TextHelper的分配方式,您可以做的另一件事。 這是一個理論上的ctor:
TextHelper(const char* input) : input_(input), copy(0) { copy = new char[sizeof(input)/sizeof(char)]; //mess with later }
~TextHelper() { delete copy; }
我認為有兩種基本方法同樣常見:a)TextHelper返回c字符串並忘記它。 用戶必須刪除內存。 b)TextHelper維護一個已分配字符串的列表,並在銷毀時解除分配它們。
現在它取決於您的使用模式。 b)對我來說似乎有風險:如果TextHelper必須釋放字符串,則在用戶使用縮短的字符串之前不應該這樣做。 您可能不知道這一點何時到來,因此您將TextHelper保持活動狀態,直到程序終止。 這導致內存使用模式等於內存泄漏。 我建議b)只有字符串在語義上屬於提供它們的類,類似於std :: string :: c_str()。 你的TextHelper看起來更像是一個不應該與處理過的字符串相關聯的工具箱,所以如果我必須在兩者之間做出選擇,我會選擇a)。 鑒於固定的TextHelper接口,您的用戶類可能是最佳解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.