老话题,拷贝构造函数!!!!

whatisyourname 2003-11-07 05:18:03
我的代码如下:
#include <iostream.h>
template <class TM>
class _Set {
TM *p;
int size;
int have;
public:
_Set(int init);
_Set(const _Set & set);
void expend(int e);
void add(TM elm);
bool In(TM elm);
bool del(TM elm);
_Set operator * (_Set obj);
_Set operator + (_Set obj);
friend ostream &operator << (ostream &s,_Set set);
};
template <class TM>
_Set<TM>::_Set(int init){
size = (init > 10) ? init:10;
p = new TM[size];
have=0;
}
/////

////
template <class TM>
_Set<TM>::_Set(const _Set & obj)
{
if(this==&obj) return;
delete[] p;
size = obj.size;
have = obj.have;
p = new TM[obj.size];
for(int i =0;i<obj.have;i++ )
p[i]=obj.p[i];
}


template <class TM>
void _Set<TM>::expend(int e){
TM * tmp = new TM[size+e];
for(int i=0;i<size;i++)
tmp[i]= p[i];
delete p;
p=tmp;
size = size + e;
}

template <class TM>
void _Set<TM>::add(TM elem)
{
if(In(elem))
return;
if(size = have)
expend(1);
p[have++] = elem;
}

template <class TM>
bool _Set<TM>::In(TM elem)
{
for(int i=0;i<have;i++)
if(p[i]==have)
return true;
return false;
}

template <class TM>
bool _Set<TM>::del(TM elem)
{
for(i=0;i++;i<have)
if(p[i]==elem)
{
p[i] = p[--have];
return true;
}
return false;
}

template <class TM>
_Set<TM> _Set<TM>::operator * (_Set obj)
{
_Set<TM> tmp(size);
for(int i=0;i<size;i++)
if(obj.In(p[i]))
add(p[i]);
return tmp;
}

template <class TM>
_Set<TM> _Set<TM>::operator +(_Set obj)
{
_Set<TM> tmp(size+obj.size);
for(int i=0; i<have;i++)
tmp.add(p[i]);
for(int k=0;k<obj.have;k++)
if(!tmp.In(obj.p[k]))
tmp.add(obj.p[k]);
return tmp;
}

void main()
{

_Set<int> x(5);
_Set<int> y(7);
x.add(3);
x.add(8);
x.add(6);
x.add(4);
x.expend(5);
x.add(9);
y.add(6);
y.add(7);
y.add(8);
y.add(9);
y.add(7);
_Set<int> z(2);

x=y;
z = (x+y);

_Set<int> a(1);

}
...全文
34 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
whatisyourname 2003-11-08
  • 打赏
  • 举报
回复
谢谢大家的参与,问题已经搞清楚了!!
qybei(qybei) 大虾说的完全正确!! :)~
Fuzzier 2003-11-07
  • 打赏
  • 举报
回复
没错,plainsong(短歌)说得对!

在没有自定义的operator=的时候,程序使用默认的值拷贝,这个时候不会调用对象的构造函数。因而会造成两个对象使用同一动态分配区,在析构时会释放两次,也同样会造成问题。

而这种问题在Thinking In C++里面已经说得很清楚了。
短歌如风 2003-11-07
  • 打赏
  • 举报
回复
Fuzzier:
还有一点你没有意识到——其时你在前面已经提到了——楼主的类少了一个析构函数。当为这个类加上一个正确的析构函数后并在其中delete[]p后,如果你不为定义一个copy assignment operator,结果仍然不会正常。
目前的错误确实是由于构造函数的释放内存操作导致的,这一点我已经说过了。去掉它并没有解决所有的问题,在返回值被赋值给局部变量时确实调用了copy assignment operator,而此时用的implicitly-declared copy assignment operator是bit-move的,它会把原对象中的p直接赋值给你的对象,但一旦这个类有了析构函数,并且在析构函数中释放了p所指向的内存,你的对象将会保存一个已被释放的内存的引用,会访问已被释放甚至可能被分配给别人的内存,会在析构时再去释放它……
Fuzzier 2003-11-07
  • 打赏
  • 举报
回复
的确,plainsong(短歌)说得对!我把问题分析错了,但终究不是copy constructor和operator=之间的关系(实际上,不用重载operator=也是可以的)。

其实,这个问题在于:
template <class TM>
_Set<TM>::_Set(const _Set & obj)
{
if(this==&obj) return;
delete[] p;=====================〉这是什么?
size = obj.size;
have = obj.have;
p = new TM[obj.size];
for(int i =0;i<obj.have;i++ )
p[i]=obj.p[i];
}

我已经把问题指出来了,实际上在构造函数到那一行为止,p都是未定义的,delete []p就是造成那个运行时内存分配错误的罪魁祸首!
bluedodo 2003-11-07
  • 打赏
  • 举报
回复
等下来看
短歌如风 2003-11-07
  • 打赏
  • 举报
回复
Fuzzier:
你意识到了在operator+中对象被析构了,但却没意识到在析构之前另一个对象——临时对象通过它被拷贝构造了。如果你的解释是正确的,那么所有返回值对象的函数(注意返回的是值而不是引用)都将不可靠——因为局部变量都会被析构,但幸好并非如此。

主要问题正如大家所说,返回值是被通过copy assignment operator赋值的,虽然楼主定义了自己的copy constructor,但并没有定义copy assignment operator,是两个不同的拷贝成员行为不一致导致了问题的出现。但copy constructor中确实有问题,就是释放未初始化的指针。而在operator+的返回过程中——正如我前面说过的——需要调用一次copy constructor(在没有进行RVO时)。

楼主的问题在于没有分清对copy constructor 和copy assignment operator调用的区别:
A a;
A b(a);//copy constructor
A c = a;//copy constructor;
A d;
d = a;//copy assignment operator
Fuzzier 2003-11-07
  • 打赏
  • 举报
回复
这个问题和copy constructor无关。

问题出在重载的运算符的返回语句上。
template <class TM>
_Set<TM> _Set<TM>::operator +(_Set obj)
{
_Set<TM> tmp(size+obj.size);
for(int i=0; i<have;i++)
tmp.add(p[i]);
for(int k=0;k<obj.have;k++)
if(!tmp.In(obj.p[k]))
tmp.add(obj.p[k]);
return tmp;
}

那个tmp在return语句之后,也就是跳出函数体的时候,被析构了-_-。

解决方法是,专门做个成员函数add(const Set& s1, const Set& s2)用来求和。

楼主的类居然没有自定的析构函数……
短歌如风 2003-11-07
  • 打赏
  • 举报
回复
把你的拷贝构造函数中的delele[]p去掉。还没有构造你释放什么?
定义一个拷贝赋值操作符:

template <class TM>
class _Set {
...
public:
_Set<TM>& operator = (const _Set<TM>& source);
};

template <class TM>
_Set<TM>& _Set<TM>::operator= (const _Set<TM>& source)
{
char _data[sizeof(_Set<TM>)];
new (_data)_Set<TM>(source);
this->~_Set<TM>();
memcpy(this, _data, sizeof(_Set<TM>));
return *this;
}
Andy84920 2003-11-07
  • 打赏
  • 举报
回复
去看任何一本C++的书都知道这样的知识!
C++ effective去看看吧!

如果你的书上没有讲这些东西.你把它扔掉吧!
liem 2003-11-07
  • 打赏
  • 举报
回复
经典之言:当你重载了复制构造函数,不要忘了重载赋值运算符。
qybei 2003-11-07
  • 打赏
  • 举报
回复
我认为你还要重载一下运算符=
我认为拷贝构造函数是在刚开始构造z的时候才会调用,像这样:
_Set<int> z=x+y;//调用拷贝构造函数

-Set<int> z(2);
z=x+y;//调用的是 operator=(...)
你上面没有重载operator=,使用的是系统自动生成的operator=
whatisyourname 2003-11-07
  • 打赏
  • 举报
回复
我的猜测是 是不是 x+y 产生了一个临时对象,我对这个临时对象的 p 进行 delete p;
产生的错误啊?
whatisyourname 2003-11-07
  • 打赏
  • 举报
回复
问题是:拷贝构造函数和 最后面的 z= x+y; 这一句。
我为了防止 x=y; 这样的语句造成指针悬挂,就重载了拷贝构造函数,在x=y;这样的语句里面
拷贝构造函数运行正常,但是 z = x+y;这一句就要出错(是运行出错,不是编译出错),
并且我已经仔细的检查了 + 号重载的函数,认为没有错误,我在拷贝构造函数的delete p;
这一举社了端点,错误就在这里,忘高手指教!!

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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