主题:[十万火急]请教有关析构函数的问题
huzhyi21
[专家分:210] 发布于 2007-03-23 15:25:00
[size=3]#include<iostream.h>
class Myclass
{
public:
Myclass(int a,int b,int c);
~Myclass();
void Getnumber();
int Getsum(Myclass m);
private:
int A,B,C;
static int Sum;
};
int Myclass::Sum(0); //静态成员函数的初始化
Myclass::Myclass(int a,int b,int c)
{
A=a;
B=b;
C=c;
Sum+=A+B+C;
cout<<"调用构造函数一次"<<A<<endl;
}
Myclass::~Myclass()
{
cout<<"调用析构函数一次"<<A<<endl;
}
void Myclass::Getnumber()
{
cout<<A<<';'<<B<<';'<<C<<endl;
}
int Myclass::Getsum(Myclass m)
{
return m.Sum;
}
void main()
{
Myclass M(3,4,5),N(5,6,7);
N.Getnumber();
cout<<M.Getsum(N)<<';'<<M.Getsum(N)<<','<<N.Getsum(M)<<endl;
}
输出结果好象与想象的不一致呀
总共调用了4次析构函数,按理说共定义了两个对象,就应该只调用2次析构函数,它怎么调用了5次析构函数呢??
在执行最后以行前就执行了析构函数3次,与形参的个数一致,且顺序一致
急急急!!![/size]
最后更新于:2007-03-23 15:49:00
回复列表 (共8个回复)
沙发
huzhyi21 [专家分:210] 发布于 2007-03-23 16:25:00
[color=FF00FF]有进展了,只知道与拷贝构造函数有关,具体思路还未理清[/color]
#include<iostream.h>
class Myclass
{
public:
Myclass(int a,int b,int c);
[color=FF00FF]Myclass(Myclass &p); [/color]
~Myclass();
void Getnumber();
int Getsum(Myclass m);
private:
int A,B,C;
static int Sum;
};
int Myclass::Sum(0); //静态成员函数的初始化
Myclass::Myclass(int a,int b,int c)
{
A=a;
B=b;
C=c;
Sum+=A+B+C;
cout<<"调用构造函数一次"<<A<<endl;
}
Myclass::~Myclass()
{
cout<<"调用析构函数一次"<<A<<endl;
}
[color=FF00FF]Myclass::Myclass(Myclass &p)
{ cout<<"调用拷贝构造函数"<<A<<endl; } [/color]
void Myclass::Getnumber()
{
cout<<A<<';'<<B<<';'<<C<<endl;
}
int Myclass::Getsum(Myclass m)
{
return m.Sum;
}
void main()
{
Myclass M(3,4,5),N(5,6,7);
N.Getnumber();
cout<<M.Getsum(N)<<';'<<M.Getsum(N)<<','<<N.Getsum(M)<<endl;
}
输出结果好象与想象的不一致呀
总共调用了4次析构函数,按理说共定义了两个对象,就应该只调用2次析构函数,它怎么调用了5次析构函数呢??
在执行最后以行前就执行了析构函数3次,与形参的个数一致,且顺序一致
急急急!
板凳
tiantangniao223 [专家分:1860] 发布于 2007-03-23 18:33:00
楼主的疑问源之于拷贝构造函数!!!!!!!!!!
但有一个对象去初始化另一个对象时会调用拷贝构造函数!!!!!
针对于楼主刚开始的那个程序,楼主可能对输出时中间的那3个析构函数感到疑问,这3个怎么回事??
其实编译器不仅调用了3个析构函数还调用了3个隐含的拷贝构造函数!!!
楼主在第二个程序中加了一个显式的拷贝构造函数,输出时楼主应该发现中间多了3个拷贝构造函数,而且是一对一的!!这些调用都是由下面的语句引起的
cout<<M.Getsum(N)<<';'<<M.Getsum(N)<<','<<N.Getsum(M)<<endl;
就以M.Getsum(N)为例吧
int Myclass::Getsum(Myclass m)
{
return m.Sum;
}
当用对象N去初始化对象m时就会调用拷贝构造函数,当函数调用完毕后这个对象m
就会被析构!(因为它的作用域仅在函数Getsum内)进而调用析构函数
另外两个同上楼主可以去找点有关拷贝构造函数的书看看
3 楼
dvdface [专家分:140] 发布于 2007-03-24 03:47:00
为什么需要析构函数???
析构函数是用来专门清理 构造函数在堆上面的分配的内存的....
因为程序的内存分为 栈和堆.
栈是系统自动分配的,变量超出作用域就会被回收,所以经常用来定义局部变量和函数的参数传值.
堆呢,是由程序员自己分配,自己管理的一块内存,操作符为new和delete.
类在构造函数里面如果用new分配了内存,那么就必须使用delete在析构函数里面释放,以免当类变量超出作用域之后,导致内存泄露,,,最后导致程序崩溃.
楼主这么个学法,有点照瓢画葫的意思,没有理解到内涵.....
再来说说你这个白痴程序吧.
void main()
{
Myclass M(3,4,5),N(5,6,7);
N.Getnumber();
cout<<M.Getsum(N)<<';'<<M.Getsum(N)<<','<<N.Getsum(M)<<endl;
}
首先你定义了两个类 M和N两个局部变量,他们都是在栈上面分配的内存
然后调用了M.Getsum两次,N.Getsum一次
当M,N超出作用域的时候,系统就会注销掉M和N,当然就会分别调用析构函数,M和N总共两次.
M.Getsum(N)这里,你是值传递,创建了一个N的临时类变量,也是在栈上面分配的,所以函数调用完毕后,这里还会调用一次.因为有两个函数,所以乘以2
N.Getsum(N)这里也是,也调用一次.
这么加起来,你这里就应该总共调用五次析构函数...........
你这本书应该是清华出的吧?完全是误认子弟.........
推荐你买本 谭浩强的C语言,仔细练习.
然后买本<C++ Premier>来读,脑中要有个清醒的认识,,,不要拘泥于程序的语法.
为什么会有C++?
为了程序的复用..
如何复用的?
请看面向对象的编程思想!!!最好买本<Head First Java>来学.
C/C++并不是为了某一种程序的设计方法而开发的,所以它适合情况的编程.
但这种自由度带来了一个问题,,,,一个新手,在还没搞清楚编程模型的情况下(大多数人都不可避免),就匆匆开始学起了语法,以为只要会了C++的语法,自己就会面向对象了.
这实际上是一个非常错误的想法.
而Java的出现很轻易的就解决了这种窘境.
它以简洁的语法结构,让你不花过多的时间拘泥于语法的问题上.而让你尽快的将它运用到面向对象编程的实践当中.
当然,Java的主要应用范围还是在网络上面.
但是作为一个掌握了面向对象思想的人,一个熟悉java语言的人,回过头来再来看C++,它的学习目的就会很明确,他能很轻易的在成堆的文字中找出关键的部分进行阅读和掌握,而不会被其淹没,搞得晕头转向.
我觉得你,应该先学Java.
学完了,学做网页,找工作也容易.
C/C++的人虽然牛x,但是牛人背后所付出的可是成倍的时间.如果你是一个大学生,在众多的课程学习中,还能把VC/C++都玩的很精通的话,无疑你这个人真的很牛x.
Java的那些语法,如果用C++,应该如何写?
Java里面的变量类型分
基本型,用=号的时候,是开辟新内存,复制值过去.
引用型,用=号赋值的时候,只是讲地址传递给了左边的变量,没有开辟新内存.
基本型在C/C++里面如何实现的呢?实际上C/C++里面所有变量的行为都是基本型.
引用型的变量在C/C++里面怎么实现的呢?把左边的变量定义为指针.
C/C++中的变量的内存是如何分配和管理的?
上面我已经讲的差不多了,但是还不够.
4 楼
dvdface [专家分:140] 发布于 2007-03-24 04:07:00
补充一下,所谓的拷贝构造函数,也是为了
值传递,无法自动复制堆上面分配的内存而设置的.
真的有必要使用他吗?
如果将C/C++按照java的约束来写程序的话,,,,,,,我看没多大的必要
5 楼
江南孤峰 [专家分:1520] 发布于 2007-03-24 08:32:00
关于拷贝构造函数:
C++拷贝对象执行按位拷贝,这是非常精确的拷贝,在某些情况下会导致问题
如某对象从堆里获得内存,某函数以该对象为实参被调用,然后在该函数内局部拷贝对象,退出函数时,将删除该对象的局部拷贝,因为是按位拷贝,同时也删除了该对象在堆里分配的内存,这样这次使用该内存区将导致错误,该对象释放时又会再次释放该内存区。C++为解决这种情况,引入拷贝构造函数,即在拷贝对象时由用户再分配一片内存区。这就是所谓的深拷贝。 一般的拷贝不涉及动态内存是不需要用户编写拷贝构造函数的C++会自动处理,这就是浅拷贝。
就象LZ的这种情况,是不需要自己编写的,如果用户写了,C++就会调用用户的拷贝构造函数,C++为用户做的事情全部交给用户,这里LZ的拷贝构造函数有问题
Myclass::Myclass(Myclass &p)
{ cout<<"调用拷贝构造函数"
<<A [color=FF0000]// 这个成员是临时对象的并没有初始化[/color]
<<endl; }
输出结果是:
调用构造函数一次3
调用构造函数一次5
5;6;7
[color=FF0000]调用拷贝构造函数4200248 // 一些莫名其妙的数[/color]
调用析构函数一次4200248
调用拷贝构造函数1245056
调用析构函数一次1245056
调用拷贝构造函数1245056
调用析构函数一次1245056
30;30,30
调用析构函数一次5
调用析构函数一次3
Press any key to continue
要么不写,要么至少象下面这样:
Myclass::Myclass(Myclass &p)
{ A=p.A;
B=p.B;
C=p.C;
cout<<"调用拷贝构造函数"<<A<<endl; }
输出结果:
调用构造函数一次3
调用构造函数一次5
5;6;7
调用拷贝构造函数3
调用析构函数一次3
调用拷贝构造函数5
调用析构函数一次5
调用拷贝构造函数5
调用析构函数一次5
30;30,30
调用析构函数一次5
调用析构函数一次3
Press any key to continue
不过没有涉及深拷贝,完全没必要自己去写。
6 楼
HEC6mon [专家分:0] 发布于 2007-03-25 11:34:00
Getsum(MyClass const & m);
7 楼
香脆饼干 [专家分:2040] 发布于 2007-03-26 12:57:00
按我的理解就是,如果数据成员没有涉及到指针类型的,就没有必要自己写拷贝函数了。如果涉及到指针,而且这个指针是用来指示动态分配的内存,那么拷贝函数就是必须的了。比如,
MyClass
{
char * m_szMyString;
public:
MyClass(char * szOther)
}
MyClass::MyClass(char *szOther)
{
int ilen=strlen(szOther)+1;
m_szMyString=(char *)malloc(ilen);
strcpy(m_szMyString,szOther);
}
MyClass A("Hello,I am A!");
MyClass B(A);调用拷贝函数
以上数据成员涉及到字符串的指针,并且这个指针是用来指向分配给自己的字符串。如果不自己写拷贝函数,那么当拷贝的时候,C++就会调用默认的拷贝函数,即一项一项的复制数据,m_szMyString=Other.m_szMyString;这样的话,B的m_szMyString跟A的m_szMyString指向的地址空间就会相同了,都是"Hello,I am A!".楼主应该可以想象以后会怎样了吧。
8 楼
幻景77 [专家分:0] 发布于 2008-01-16 21:12:00
输出结果是:
调用构造函数一次3
调用构造函数一次5
5;6;7
调用拷贝构造函数-858993460 // 乱码,至于为什么你可以看一下江南孤峰的回帖,说得很清楚
//为什么会调用拷贝构造函数呢,看一下下面的函数
//Myclass::Getsum(Myclass m)
//调用该函数它首先要进行形实结合,即使实参初始化m
//这里的参数如果是引用的话,情况就不一样了,你可以试一下
//那么就只调用两次拷贝,两次析构了
调用析构构造函数-858993460
//由于m的作用域只在该函数内,函数调用完毕马上析构
//下面两次的调用情况同
调用构造函数一次1245056
调用析构函数一次1245056
调用拷贝构造函数1245056
调用析构函数一次1245056
30;30,30
调用析构函数一次5
调用析构函数一次3
我来回复