1月24日:
前言:最近在写一个RTMP推流的DEMO,我用的是Message Loop的形式来推流,为了在网络差的时候能够动态丢包,需要在消息队列到达阀值的时候丢掉以前的消息。消息只是个int型,但为了使Message Loop类通用,消息对应的数据是Void,此时动态丢消息就会变成delete Void,当我写这行代码的时候,我惊了,这样真的能释放掉一个Object* 对象?
答案是否。如果直接用delete void,释放空间,这其实和c语言里面的free功能一样,当一个普通指针来释放,只会释放掉void指向的内存,如果void*对象里面还有其他指针,就不能通过析构函数来释放。用一段代码是说明:
class mycalss
{
public:
mycalss() : data(new int[100]){}
~mycalss()
{
delete[] data;
}
private:
int *data;
};
int main()
{
mycalss* pobj =new mycalss;
delete pobj;
return 0;
}
这里,不会有内存泄漏产生,因为 delete pobj指针的时候,会先调用 myclass 的析构函数释放掉 data*的那块堆内存,然后再释放 sizeof(myclass) 大小的堆内存。如果直接这样用:
int main()
{
void* pobj = new mycalss;
delete pobj;
return 0;
}
这里,会有400字节的内存泄漏,也就是只释放了 sizeof(myclass) 大小的那块堆内存,而pobj->data那块并未被释放,为何?因为析构函数未被调用!!!
所以,请记住,永远不要 delete void,消除所有warning: deleting 'void' is undefined [enabled by default]警告!!!
其实到这里也有一个同样问题,为什么要把父类的析构函数声明成virtual(虚函数)?我这里就不再说明,引用知乎上一个提问。
现在简单说下我最开始遇到的问题。起初,我自然想到share_ptr,但是想了下,这其实和模板一样依然限制里数据类型,看来只能和handle一样写个虚函数,让子类来实现释放代码了。
贴上简短代码:
class looper {
public:
looper();
virtual ~looper();
//flush 是否清空消息队列
void post(int what, void *data, bool flush = false);
void quit();
virtual void handle(int what, void *data);
private:
virtual void addmsg(loopermessage *msg, bool flush);
static void* trampoline(void* p);
void loop();
protected:
std::deque< loopermessage * > _msgQueue;
pthread_t worker;
sem_t headwriteprotect;
sem_t headdataavailable;
bool running;
};
这是loop父类,里面消息队列没有限制。
//固定长度loop,超出loop,删除最老消息
class FixedLoop: public looper
{
public:
FixedLoop(int messageLen);
private:
virtual void eraseMsg(int what, void *data);
virtual void addmsg(loopermessage *msg, bool flush)
{
sem_wait(&headwriteprotect);
if (flush) {
_msgQueue.clear();
}
LOGV("size =%d max =%d",_msgQueue.size(),_MaxMsgLen);
if(_msgQueue.size() >= _MaxMsgLen) //移除一个消息
{
loopermessage *tempMsg = _msgQueue.front();
_msgQueue.pop_front();
eraseMsg(tempMsg->what,tempMsg->obj);
delete tempMsg;
}
_msgQueue.push_back(msg);
LOGV("post msg %d", msg->what);
sem_post(&headwriteprotect);
sem_post(&headdataavailable);
}
private:
int _MaxMsgLen;
};
这是固定长度loop子类。
1.29修改:
卧槽,我宛如一个智障,有那么麻烦?不知道数据类型?你不会写一个消息基类,其他的消息都要继承你提供的基类,这样不就可以通过delete基类来释放派生类。突然好鄙视自己,感觉学了这么久c++都白学了,心情无比复杂 ······
2.22:
Loop类源码可以看这篇文章。