作为介绍,请注意我是一名Java程序员,仍然习惯于C ++中的内存管理问题。
我们有一个基类,用于将对象编码为ASCII字符串。本质上,该类使用stringstream
类成员将不同的数据类型转换为一个长字符串,然后将char*
返回给包含编码对象数据的调用者。
在测试内存泄漏时,我发现我们使用的实现似乎容易产生内存泄漏,因为用户必须始终记住删除方法的返回值。以下是代码相关部分的摘录:
char* Msg::encode()
{
// clear any data from the stringstream
clear();
if (!onEncode()) {
return 0;
}
// need to convert stringstream to char*
string encoded = data.str();
// need to copy the stringstream to a new char* because
// stringstream.str() goes out of scope when method ends
char* encoded_copy = copy(encoded);
return encoded_copy;
}
bool Msg::onEncode(void)
{
encodeNameValue(TAG(MsgTags::TAG_USERID), companyName);
encodeNameValue(TAG(MsgTags::TAG_DATE), date);
return true;
}
bool EZXMsg::encodeNameValue(string& name, int value)
{
if(empty(value))
{
return true;
}
// data is stringstream object
data << name << TAG_VALUE_SEPARATOR << value << TAG_VALUE_PAIRS_DELIMITER;
return true;
}
char* copy(string& source) {
char *a=new char[source.length() +1];
a[source.length()]=0;
memcpy(a,source.c_str(),source.length());
return a;
}
更新
嗯 - 我应该更准确地了解encode()
的结果是如何消耗的。它传递给boost:async_write,并且程序崩溃,因为我认为在async_write完成之前字符串超出了范围。看起来我需要将返回的字符串复制到一个类成员,该成员在发送消息(?)的类的生命周期中是活动的。
这是实际使用encode()
方法的方式(在我将返回值更改为string
之后):
void iserver_client::send(ezx::iserver::EZXMsg& msg) {
string encoded = msg.encode();
size_t bytes = encoded.length();
boost::asio::async_write(socket_, boost::asio::buffer(encoded, bytes), boost::bind(&iserver_client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
看起来正确的方法是将字符串的队列/列表/向量维护为异步写入。如上所述here(以及boost chat_client示例中)。 (但这是一个单独的问题。)
答案 0 :(得分:2)
对于这个问题: 在你的复制功能中你返回一个指向堆内存的指针!所以用户可能会创建内存泄漏,我想你不能使用这个复制功能,你可以在你的编码函数中这样做:
return data.str();
如果你想得到一个char *,你可以使用string:c_str()的成员函数, 就像这样:
string ss("hello world");
const char *p = ss.c_str();
如果使用堆栈字符串对象,则不会产生内存泄漏,
答案 1 :(得分:1)
您只需返回std::string
即可。无论如何你还有一个:
string Msg::encode()
{
// clear any data from the stringstream
clear();
if (!onEncode()) {
return string{};
}
return data.str();
}
然后调用者看起来像:
Msg msg;
msg.userID = 1234;
send(msg.encode().c_str());
答案 2 :(得分:1)
实现“自动”删除的唯一方法是使用超出范围的堆栈变量(在某种程度上)。事实上,这通常是保证删除的唯一方法,例如,即使例外情况也是如此。
正如其他人提到的那样std::string
工作正常,因为char *
归堆栈分配string
所有,这将删除char *
。
这通常不起作用,例如非char *
类型。
一个好的解决方案是使用Boost的scoped_array
,如下所示:
{
Msg msg;
msg.userID = 1234;
scoped_array<char> encoded(msg.encode());
send(encoded.get());
// delete[] automatically called on char *
}
scoped_ptr
对非数组类型的工作方式类似。
仅供参考:您应该使用delete[] encoded
来匹配new char[source.length() +1]
答案 3 :(得分:1)
虽然使用std::string
可以充分解决您的特定问题,但一般的解决方案是返回std::unique_ptr
而不是原始指针。
std::unique_ptr<char[]> Msg::encode() {
:
return std::unique_ptr<char[]>(encoded_copy);
}
用户将在调用时获得新的unique_ptr
:
auto encoded = msg.encode();
send(encoded.get());
当encoded
超出范围并被销毁时,内存将自动释放。