2013-07-14 55 views
2

作为一个介绍,请注意我是一个Java程序员,仍然习惯于C++中的内存管理问题。如何使这段代码更少的内存泄漏?

我们有一个基类,用于将对象编码为一串ASCII字符。本质上,类正在使用类成员将不同的数据类型转换为一个长字符串,然后将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; 
    } 

UPDATE

嗯 - 我应该是关于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示例中)。 (但这是一个单独的问题。)

+1

这取决于期望的API是什么。一个大问题 - 返回的'char *'应该有效多久,谁应该释放与之关联的内存? –

+5

为什么不直接从'encode()'方法返回一个'string'(或者传入一个'string'的引用)? –

+1

不要使用'char *'作为返回类型;事实上,避免原始的'char *'字符串并使用'std :: string'。使用RAII(资源获取初始化)确保异常和正常使用避免泄漏。尽可能避免“新”;当你不能时非常小心。 –

回答

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()); 
+1

将'0'返回给字符串对象会使其崩溃。因为你是用空指针初始化一个字符串 – billz

+0

@billz:谢谢你指出。 – Mankarse

+1

'返回{};':) – chris

2

对于这个问题: 在复印功能返回一个指向堆内存中,因此用户可能造成内存漏,我想你不能用这个复制功能,您可以在编码FUNC做就像这样:如果你想获得一个char *

return data.str(); 

,您可以用〜应变的成员函数G:c_str(), 就像这样:

string ss("hello world"); 
const char *p = ss.c_str(); 

如果您使用堆栈字符串对象就不会造成内存泄漏,

1

实现的唯一途径“自动”删除是一个堆栈变量(在某种程度上)超出范围。实际上,例如,即使在例外的情况下,这也是保证删除的唯一方法。

正如其他人提到的std::string工作得很好,因为char *归堆栈分配string,这将删除char *

这通常不起作用,例如非char *类型。

RAII(资源获得即初始化)可以处理这些问题如内存管理,锁定获取/释放等

有用习惯一个好的解决办法是如下使用Boost的scoped_array

{ 
    Msg msg; 
    msg.userID = 1234; 
    scoped_array<char> encoded(msg.encode()); 
    send(encoded.get()); 
    // delete[] automatically called on char * 
} 

scoped_ptr的工作方式类似于用于非数组类型。

FYI:你应该使用delete[] encoded边使用std::string作品充分为您的具体问题,以配合new char[source.length() +1]

+0

啊 - 谢谢你的提示有关删除[]编码。我不知道。 –

1

,一般解决方法是返回一个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超出范围和被破坏的内存将被自动释放。

+0

使用unique_ptr和boost :: shared_ptr有什么区别? –

+1

@SamGoldberg:一个'unique_ptr'不能被复制,只能被移动,所以它唯一的指向对象的指针。'std :: shared_ptr'(与boost :: shared_ptr几乎相同)与'unique_ptr'相比具有一定的开销(引用计数),并且不适用于数组,而'unique_ptr'具有数组的专用版本。 –

+0

感谢您的解释。这一切都开始变得更有意义。 –