在改变c字符串时避免内存泄漏

时间:2009-07-26 22:07:21

标签: c++ memory-management connection-string cstring

出于教育目的,我在一些测试程序中使用cstrings。我想用占位符缩短字符串,例如“......”。

也就是说,如果我的最大长度设置为13,"Quite a long string"将变为"Quite a lo..."。此外,我不想销毁原始字符串 - 因此缩短的字符串必须是副本。< / p>

下面的(静态)方法就是我想出来的。我的问题是:为我的缩短字符串分配内存的类是否也应该负责释放它? 我现在所做的是将返回的字符串存储在单独的“用户类”中,并将内存释放到该用户类。

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;
}

8 个答案:

答案 0 :(得分:6)

这样的函数的标准方法是让用户传入char []缓冲区。您可以在诸如sprintf()之类的函数中看到这一点,它将目标缓冲区作为参数。这允许调用者负责分配和释放内存,将整个内存管理问题保存在一个地方。

答案 1 :(得分:5)

为了避免缓冲区溢出和内存泄漏,在这种情况下,总是使用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 ++视为一种非常不同的语言更合适。

答案 2 :(得分:2)

我一直很高兴返回指向本地分配内存的指针。我喜欢对任何在清理方面称呼我职能的人保持健康的不信任。

相反,你是否考虑过接受一个缓冲区,你要复制缩短的字符串?

例如

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)。

答案 3 :(得分:2)

您真的应该使用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));

答案 4 :(得分:1)

最灵活的方法是返回一个包装已分配内存的辅助对象,以便调用者不必担心它。该类存储指向内存的指针,并具有复制构造函数,赋值运算符和析构函数。

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来实现此目的。

答案 5 :(得分:0)

在您的示例中,调用者别无选择,只能负责释放已分配的内存。

然而,这是一个易于使用的习惯用语,我不建议使用它。

允许您使用几乎相同代码的一种替代方法是将shortened更改为引用的计数指针,并让方法返回引用的计数指针而不是裸指针。

答案 6 :(得分:0)

编辑:不,我错了。我误解了你想要做的事情。调用者必须删除实例中的内存。

C ++标准规定删除0 / NULL什么都不做(换句话说,这样做是安全的),所以你可以删除它,无论你是否曾经调用过这个函数。编辑:我不知道这是怎么被遗漏的......你的另一个选择是放置删除。在这种情况下,即使它是错误的形式,你也应该使用placement new来保持分配/释放在同一个地方(否则不一致会使调试变得荒谬)。

那说,你是如何使用代码的?我不知道你何时会多次调用它,但如果你这样做,如果你不记得每个不同的内存块,就会有潜在的内存泄漏(我认为)。

我只会使用std::auto_ptrBoost::shared_ptr。它在出路时自行删除,可与char *一起使用。

考虑到如何分配TextHelper,您还可以做其他事情。这是一个理论上的人:

TextHelper(const char* input) : input_(input), copy(0) { copy = new char[sizeof(input)/sizeof(char)]; //mess with later }
~TextHelper() { delete copy; }

答案 7 :(得分:0)

我认为有两种基本方法同样常见: a)TextHelper返回c字符串并忘记它。用户必须删除内存。 b)TextHelper维护一个已分配字符串的列表,并在销毁时解除分配它们。

现在它取决于您的使用模式。 b)对我来说似乎有风险:如果TextHelper必须释放字符串,则在用户使用缩短的字符串之前不应该这样做。您可能不知道这一点何时到来,因此您将TextHelper保持活动状态,直到程序终止。这导致内存使用模式等于内存泄漏。我建议b)只有字符串在语义上属于提供它们的类,类似于std :: string :: c_str()。你的TextHelper看起来更像是一个不应该与处理过的字符串相关联的工具箱,所以如果我必须在两者之间做出选择,我会选择a)。在给定固定的TextHelper接口的情况下,您的用户类可能是最佳解决方案。