queue模板的问题

xysome 2004-03-20 10:44:56
queue定义如下:
typedef queue<tagRecord*> CMyQueue;
CMyQueue myQueue;
在某个函数中增加元素的代码如下:
tagRecord *myRecord;
myRecord=new tagRecord;
... //对myRecord进行其他处理
myQueue.push();
问题1:我这样增加元素对否?这样做会有内存泄漏。
在另一个函数中弹出元素的代码如下:
tagRecord *myRecord;
myRecord=myQueue.front();
... //对myRecord进行其他处理
delete myRecord;
问题2:这样弹出元素对否?
问题3:typedef queue<tagRecord*> CMyQueue方式和typedef queue<tagRecord> CMyQueue方式有什么区别?
问题4:使用queue模板要注意什么?
谢谢!

...全文
330 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
wingfiring 2004-03-22
  • 打赏
  • 举报
回复
nickadams(nickadams) 的观念不大对。
stl要求容器的成员应该具备值语义,也就是是说,应该具备拷贝构造、赋值等能力,并且,不应该出现副作用。简单说,就是满足ADT的要求。
事实上,stl是主张用对象的----大多数情况下,我们的对象并不大。
Wolf0403 2004-03-21
  • 打赏
  • 举报
回复
O 的定义改成
struct O
{
static int cnt;
void * operator new(size_t _sz){++cnt; return ::operator new(_sz);}
void operator delete(void * _p){--cnt; return ::operator delete(_p);}
};
就可以发现:调用 operator new 分配了20 个内存空间,20 个空间全部泄漏。
Wolf0403 2004-03-21
  • 打赏
  • 举报
回复
nickadams:
使用模版容器时最好还是用指针,除非是简单数据类型,或者简单对象。
不要误导。使用标准容器保存对象指针就完全放弃了标准容器自动管理对象生命期的能力,而且也不再异常安全。参考以下代码:

struct O
{
static int cnt;
~O(){++cnt;}
};

int O::cnt = 0;

int main()
{
{
vector<O *> vip;
for (int i = 0; i < 20; ++i)
vip.push_back(new O);
} // vip 到这里过期,所有元素再也不可访问。
cout << O::cnt << endl; // Dev-c++ 下结果:0。没有调用任何析够函数。
system("PAUSE");
}
b_horse 2004-03-21
  • 打赏
  • 举报
回复
不错,在一些应用中,比如简单类型对象(与virtual无关的对象),内建类型(如int等),使用指针也许有其一定的优势(有时候我也会想怎么用),如上面所说的可以节省直接的对象拷贝。但这样使用container的弊端更多。第一,使用指针类型的container,你必须为每个对象的创建、销毁花费更多的心思,而在container中使用对象,这些工作都是“免费”的;第二,面对“多态”带来的冲击,指针型的container可能(好像和不同的compiler有关)会带来runtime时的错误;第三,如果我上面关于指针使用范围的论述不错的话,将意味着你将得到一块连续的内存放置你的系列“指针”,而其所指的“对象”将分布在堆上不同的位置,这时,如果你在程序中频繁地访问这些对象的话,你在性能上的损失将是用省却copy constructor所无法弥补的;第四,即使你真的想用指针的话,也请使用boost中的那个吧,指针的这个东西,你在标准库所提供的container也好、algorithm也好,真的见过指针的出现吗?总有着感觉,标准库的出现从很大程度上是想让使用者戒掉对指针的依赖,尽管我们有时不得不用到它们。
b_horse 2004-03-21
  • 打赏
  • 举报
回复
首先,在这个问题上我们必须澄清一点:在标准库中,标准容器的操作核心是copy!!!这就是说,但你要在程序中使用标准容器时,你要为由此产生的代价做好准备,注意是copy出来,copy进去,一切都是copy!因而,在你使用标准容器之前,检查是否需要提供用户定义的copy构造函数(及相应的赋值operator),以及用户定义的copy构造函数是否符合程序的要求(这种要求主要依据具体的应用而定),都将是你的“责任”:也许对于简单类型的对象或是类似struct的对象我们可以避免定义copy构造函数(因为compiler为你做的要比你自己做的好^_^),也许你必须定义一个(比如当对象中含有reference counting成分的时候),但请一定要做好准备!
本问题关注的交点是在“是否应该在container中使用指针”,实践中我们很难用一个标准来解答这个问题,或许包含指针的container在你的应用中你用得很好,没错,“真棒!”,这可能就是你的评价。但请注意,你的使用肯定是局限在sequence container中(比如,vector,deque, string),如果是associative container(如,map,set,hash),你的运气就没有那么好了,因为后者是有序container,使用指针作为元素,可能你省了copy constructor的定义,但你得为专门的排序函数担心!好在楼主的问题踢得很好,queue,呵呵,实际上是deque的派生container,那我们还有什么可以担心的?!
xysome 2004-03-21
  • 打赏
  • 举报
回复
非常精彩,增长了很多知识,谢谢大家!
nickadams 2004-03-21
  • 打赏
  • 举报
回复
怎么叫误导?
在容器中使用指针需要自己管理对象这是众所周知的事情,楼主也已经得到答案。

"直接使用对象,好处是免去了创建和删除对象的麻烦,但坏处多多..."

我已经说得很明白了,并不是不可以直接使用对象,我只是指出潜在的危险而已。

class O
{
char* pstr;

public:
O() { pstr = NULL; }
~O()
{
TRACE("delete %p\n", pstr);
delete pstr;
}

void Allocate(int size)
{
pstr = new char[size];
TRACE("new %p\n", pstr);
}
void Copy(char* p)
{
TRACE("copy %p\n", pstr);
strncpy(pstr, p, 100);
}
};

如果没有拷贝构造函数和operator=,直接把这个类放进STL容器使用就不仅仅是内存泄漏的问题了。

void main()
{
list<O> l;
{
O o;
o.Allocate(100);
l.push_front(o);
}
O o1 = l.front();
o1.Copy("abc");
}

从{}返回时,ptr分配的内存已经被释放了,l中保存的o使用的是无效地址。这样你必须为O类加上拷贝构造函数和operaor=,like

O(const O& o)
{
*this = o;
}

O& O::operator=(const O& o)
{
if (this != &o)
{
delete pstr;
pstr = NULL;
if (o.pstr != NULL)
{
pstr = new char[strlen(o.pstr)+1];
memcpy(ptr, o.ptr, strlen(o.pstr)+1);
}
}
return *this;
}

如果使用指针的话根本不必添这么麻烦。而且,如果你有从O派生子类的话,它们都需要加这些东东。
nickadams 2004-03-20
  • 打赏
  • 举报
回复
使用模版容器时最好还是用指针,除非是简单数据类型,或者简单对象。
直接使用对象,好处是免去了创建和删除对象的麻烦,但坏处多多。除了构造、拷贝对象的开销外,把对象作为参数或返回值在queue的函数中使用都是很低效的。如果你的对象中有用到动态分配内存的话,你还往往需要为你的类加上拷贝构造函数并重载operator=,否则编译器会使用缺省的拷贝构造函数和operator=,然后你就陷入了debug的深渊。
Wolf0403 2004-03-20
  • 打赏
  • 举报
回复
只是 vector 吧。。。在标准容器中使用指针,容器不会清理指针映射的堆对象,需要手工删除。
除了对于大对象配合 vector 之外,都应该把对象直接放入容器中。
大对象建议用 boost::share_ptr 之类东西。
oyd 2004-03-20
  • 打赏
  • 举报
回复
问题4:使用queue模板要注意什么?
B.S推荐在容器中使用对象指针作为容器的元素,
所以你的问题3可以用typedef queue<tagRecord*> CMyQueue,好处是可以方便的控制对象的声明周期。
而用typedef queue<tagRecord> CMyQueue,一来效率受到损害,涉及到对象构造的开销,第二,就是上面说过的对象生存期问题。
xstring 2004-03-20
  • 打赏
  • 举报
回复
queue是通过list来实现的你调用的
1、正确
2、少调用了一个myQueue.pop ();
3、使用queue <tagRecord>的话不用调用new

你的程序可以变成这样

tagRecord myRecord;
...; // 处理
myQueue.push (myRecord);

tagRecord *myRecord = &myQueue.front ();
//处理
myQueue.pop ();
// delete也不用调
sharkhuang 2004-03-20
  • 打赏
  • 举报
回复
反正要概念清晰,小心使用.

24,855

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧