出于教育目的,我在一些测试程序中使用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;
}
答案 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[]
而不是free
或delete
,甚至是较低级别的操作系统调用。将内存管理留给调用者只是更灵活,更不容易出错。
返回指向目标的指针只是一个很好的例子,允许你做这样的事情:
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_ptr
或Boost::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接口的情况下,您的用户类可能是最佳解决方案。