注册 登录
编程论坛 C++教室

不散分,100分只给一个人(疑惑在代码中)

QQ346957135 发布于 2011-10-02 11:21, 1426 次点击
程序代码:
#include<iostream>
using namespace std;
class CStrtemp
{
public:
    CStrtemp(char *s);
    CStrtemp(const CStrtemp&);
    ~CStrtemp();
    void show();
    void set(char *s);
private:
    char *str;
};
CStrtemp::CStrtemp(char *s)
{
    cout<<"constructor."<<endl;
    str=new char[strlen(s)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,s);
}

CStrtemp::CStrtemp(const CStrtemp& temp)
{
    cout<<"copy constructor."<<endl;
    str=new char[strlen(temp.str)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,temp.str);
}

CStrtemp::~CStrtemp()
{
    cout<<"destructor."<<endl;
    if(str!=NULL)
    {
        delete[] str;
    }
}

void CStrtemp::show()
{
    cout<<str<<endl;
}
void CStrtemp::set(char *s)
{
    delete[] str;
    str=new char[strlen(s)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,s);
}
CStrtemp Input(CStrtemp temp)
{
    char s[20];
    cout<<"Please input the string:";
    cin>>s;
    temp.set(s);
    return temp;
}

int main()
{
    CStrtemp A("hello");
    A.show();
    CStrtemp B=Input(A);//此处对象B的创建不需要调用复制构造函数吗,形式上为对象复制的一般格式<类名><对象2>=<对象1>;,为什么没调用?
    A.show();
    B.show();
    return 0;
}


















只有本站会员才能查看附件,请 登录
21 回复
#2
hoho5682011-10-02 14:15
这是C++的代码优化:
如果一个临时对象作为返回值被立即赋给另一个未构造对象,这个临时对象本身将被构造到被赋值对象在内存中的位置,即引用。
所以少了一次构造函数的调用。然而,实际上是减少了一次拷贝构造函数和一次临时对象的析构。相当于没有产生临时对象,但事实上是产生了临时对象。
调用复制构造函数都是真正的产生了临时对象。

可能有点绕,说的简单一点就是: CStrtemp B=Input(A);相当于B成为了临时对象的引用(和引用还是由区别,毕竟B不是引用),所有没有调用拷贝构造函数。同样的,也没有调用B的构造函数;

如果你细心的话应该注意到另外一个问题,就是你的临时对象也没有调用析构函数;B=Input(A);这个函数返回时会调用拷贝构造函数,同时函数里面的对象调用了析构函数,同时临时对象在赋值(或者初始化)完成后,应该调用析构函数,所以少一个析构函数。原因就是并没有析构,而是将这部分内存空间给了对象B;
程序代码:

int main()
{
    CStrtemp A("hello");
    A.show();
    CStrtemp B("fdf");
    B = Input(A);
    system("pause");
    return 0;
}

对你的程序稍微修改,就可以看到函数返回过程是有两个析构函数的,分别是函数里面的对象和临时对象。这就不难理解了吧。
#3
pangding2011-10-02 15:10
你怎么断言的它没有调用呀。


我觉得是下面这个流程。不知道和楼主理解的一样不一样。

constructor.  构造A
hello  输出A
copy constructor.   构造input函数的那个形参temp
Please input the string:abcd
copy constructor.  构造B
destructor.  析构temp
hello  输出A
abcd 输出B
destructor.  析构B
destructor.  析构A


[ 本帖最后由 pangding 于 2011-10-2 15:11 编辑 ]
#4
QQ3469571352011-10-02 15:48
回复 2楼 hoho568
你的第二句话貌似能合理的解释我的问题,但不够充分;下面的说明我想指出一点,在函数内返回对象时调用了复制构造函数创建临时对象,再将临时对象的值复制给B,然后临时对象就析构了。所以有输出的第一个destructor。关于那个形参,传递实参时调用了一次复制构造函数,但没有析构。书上没说明这点,我猜想形参随着函数的调用结束本来就该释放内存的,所以没必要析构。我想再等等,看有没有跟充分的解释。
#5
QQ3469571352011-10-02 15:50
回复 3楼 pangding
第二个copy constuctor不是构造B而是构造临时对象才调用的;书上有明确说明。
#6
hoho5682011-10-02 16:21
以下是引用QQ346957135在2011-10-2 15:48:53的发言:

你的第二句话貌似能合理的解释我的问题,但不够充分;下面的说明我想指出一点,在函数内返回对象时调用了复制构造函数创建临时对象,再将临时对象的值复制给B,然后临时对象就析构了。所以有输出的第一个destructor。关于那个形参,传递实参时调用了一次复制构造函数,但没有析构。书上没说明这点,我猜想形参随着函数的调用结束本来就该释放内存的,所以没必要析构。我想再等等,看有没有跟充分的解释。



晕,什么叫随函数结束了释放内存,没有析构。我不是给你举例子了吗。你看我给你修改的程序,你就需要析构的。。。
你的这个概念不是很正确,所有的类对象只要定义,就会析构。内存释放就是一个析构的过程。。。再好好想想吧。。

看看我给你的修改的例子,好好理解一下吧。

[ 本帖最后由 hoho568 于 2011-10-2 16:22 编辑 ]
#7
QQ3469571352011-10-02 16:30
回复 6楼 hoho568
那我想问一下,形参temp传递给临时对象后跑哪儿了,如果析构了的话怎么没输出destructor?
#8
QQ3469571352011-10-02 16:49
回复 6楼 hoho568
你给的例子运行出现了,我分析了一下在对象的赋值时,类的数据成员中不能包括动态分配的数据,否则在最后析构时由于“浅复制”会出现错误,程序不能正常运行。
#9
hoho5682011-10-02 16:56
以下是引用QQ346957135在2011-10-2 16:30:58的发言:

那我想问一下,形参temp传递给临时对象后跑哪儿了,如果析构了的话怎么没输出destructor?



没有输出吗?不是输出了吗。
#10
QQ3469571352011-10-02 16:58
回复 9楼 hoho568
输出的第一个destructor是析构临时对象的,后面两个析构B和A,哪儿输出了?
#11
mengcan5552011-10-02 17:00
Input(A);把A传递过去,对A有影响吗?
#12
hoho5682011-10-02 17:02
以下是引用QQ346957135在2011-10-2 16:49:19的发言:

你给的例子运行出现了,我分析了一下在对象的赋值时,类的数据成员中不能包括动态分配的数据,否则在最后析构时由于“浅复制”会出现错误,程序不能正常运行。

对,是存在那个问题。我写这个例子主要是为了验证临时对象的析构过程。
只有本站会员才能查看附件,请 登录


而在你的程序中之所以没有被析构,就是相当于把临时对象给了B;即B指向这个临时变量,从而完成初始化过程。
类似于引用。

注意一下,因为system("pause");所以A和B的析构函数还没有调用,不要误解了。

[ 本帖最后由 hoho568 于 2011-10-2 17:03 编辑 ]
#13
hoho5682011-10-02 17:17
以下是引用QQ346957135在2011-10-2 16:58:16的发言:

输出的第一个destructor是析构临时对象的,后面两个析构B和A,哪儿输出了?


形参temp,就是你函数里面的那个temp。
#14
hoho5682011-10-02 17:19
一般而言,拷贝构造函数就是构造函数+赋值操作; 所以会生成一个临时变量。
对象作为形参传入函数中,构造的那个对象就是函数里面的实参。所以中间没有临时变量。。
#15
QQ3469571352011-10-02 17:29
回复 12楼 hoho568
你的意思是说临时对象没析构吗?
#16
QQ3469571352011-10-02 17:42
回复 14楼 hoho568
我修改了部分代码明白了形参在函数结束后会析构,但临时对象到底是怎么一回事,好像复制给B后没有析构,我有点不明白,应该析构的。
#17
Mack22011-10-02 17:54
constructor.  构造A
hello  输出A
copy constructor.   构造input函数的那个形参temp
Please input the string:abcd
copy constructor.  构造B
destructor.  析构temp
hello  输出A
abcd 输出B
destructor.  析构B
destructor.  析构A
过程很清晰,楼主怎么说临时对象temp没有析构呢。我修改一点你的程序,更容易理解
程序代码:
#include<iostream>
using namespace std;
class CStrtemp
{
public:
    CStrtemp():str(NULL){
    }//无参数构造函数
    CStrtemp(char *s);
    CStrtemp(const CStrtemp&);
    ~CStrtemp();
    void show();
    void set(char *s);
    CStrtemp Input();
private:
    char *str;
};
CStrtemp::CStrtemp(char *s)
{
    cout<<"constructor."<<endl;
    str=new char[strlen(s)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,s);
}

CStrtemp::CStrtemp(const CStrtemp &temp)
{
    cout<<"copy constructor."<<endl;
    str=new char[strlen(temp.str)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,temp.str);
}

CStrtemp::~CStrtemp()
{
    cout<<"destructor."<<endl;
    if(str!=NULL)
    {
        delete[] str;
    }
}

void CStrtemp::show()
{
    cout<<str<<endl;
}
void CStrtemp::set(char *s)
{
    delete[] str;
    str=new char[strlen(s)+1];
    if(!str)
    {
        cout<<"Allocation Error."<<endl;
        exit(1);
    }
    strcpy(str,s);
}
CStrtemp CStrtemp::Input()
{
    char s[20];
    cout<<"Please input the string:";
    cin>>s;
    CStrtemp temp;//创建临时对象
    temp.set(s);
    return temp;//调用拷贝构造函数之后调用析构函数析构temp
}

int main()
{
    CStrtemp A("hello");
    A.show();
    CStrtemp B=A.Input();//此处对象B的创建不需要调用复制构造函数吗,形式上为对象复制的一般格式<类名><对象2>=<对象1>;,为什么没调用?
    A.show();
    B.show();
    return 0;
}

#18
hoho5682011-10-02 18:03
你可以按照楼上的方式理解。其实那个临时变量就是给了B。。。前面已经解释的很清楚了,相当于把那块内存空间给了B。
#19
hoho5682011-10-02 18:06
以下是引用QQ346957135在2011-10-2 17:42:22的发言:

我修改了部分代码明白了形参在函数结束后会析构,但临时对象到底是怎么一回事,好像复制给B后没有析构,我有点不明白,应该析构的。


这个临时变量就是B。。相当于把这块内存给了B。。也就是B的值。。所以我前面说相当于引用。。
假设这个临时变量是t ..相当于 b = &t;就是说B就是这块内存空间。。。
楼上有几位的解释说那个拷贝构造函数是构造B,不能完全说是错误的。。因为那块的确给了B。不过是临时对象的。
#20
QQ3469571352011-10-02 18:13
回复 17楼 Mack2
我说的临时对象不是temp,temp是形参,临时对象是返回对象时临时创建的。
#21
QQ3469571352011-10-02 19:03
我懂了,刚在网上看到的。和hoho说的差不多。这部分没学好的请耐心看一下。
http://blog.
#22
pangding2011-10-03 15:01
以前花过一段时间研究临时对象的问题。

不过楼主说的那种情况,我就是按三楼那么理解的。而且这种情况,确实临时对象也没有出现。
这么解释也比较利于初学者听明白。
1