簡體   English   中英

在改變c字符串時避免內存泄漏

[英]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_textconst 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[]而不是freedelete ,甚至是更低級別的操作系統調用。 將內存管理留給調用者只是更靈活,更不容易出錯。

返回指向目標的指針只是一個很好的例子,允許你做這樣的事情:

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_ptrBoost::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.

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