C++对象的构造、赋值和析构

Posted on 2006-11-25 16:08 iceboundrock 阅读(630) 评论(1)  编辑  收藏 所属分类: 算法与数据结构学习

C++ C#/java 有很多区别,其中最大的区别当数对内存的管理。

C++ 中,类的使用者决定了类的实例内存会如何分配,分配在堆上还是栈上。我们先看一段例子程序:

 

#include "stdio.h"

 

class Demo{

public :

    int i;

    char* objName;

    Demo(){

        objName = "Default object.";

        printf("%s, objName = %s\r\n", "Enter Demo default ctor. method.", objName);

       

        i = 1000;

    }

 

    Demo(int ival, char* name){

        printf("%s,i = %d, objName = %s\r\n", "Enter Demo(int ival) ctor method", ival, name);

        i = ival;

        objName = name;

    }

 

    Demo(const Demo& d){

        printf("%s\r\n", "Enter Demo copy ctor method.");

        i = d.i;

        objName = "copied d";

    }

 

    ~Demo(){

        printf("%s, i = %d, objName = %s\r\n", "Enter Demo dector. method" , i, objName);

    }

};

 

Demo& testMethod0(){

    printf("%s\r\n", "Enter testMethod0.");

    Demo d(0, "d in testMethod0");

    printf("%s\r\n", "Exit testMethod0.");

    return d;

}

 

Demo testMethod1(){

    printf("%s\r\n", "Enter testMethod1.");

    Demo d(1, "d in testMethod1");

    printf("%s\r\n", "Exit testMethod1.");

    return d;

}

 

Demo* testMethod2(){

    printf("%s\r\n", "Enter testMethod2.");

    Demo *d = new Demo(2, "d in testMethod2");

    printf("%s\r\n", "Exit testMethod2.");

    return d;

}

 

int main(int argc, _TCHAR* argv[])

{

    Demo d;

    d = testMethod1();

 

    Demo& d1 = testMethod0();

 

    Demo d2(999, "d1");

 

    Demo* d3 = testMethod2();

 

    printf("d.i = %d\r\n", d.i);

    printf("d1.i = %d\r\n", d1.i);

    printf("d2.i = %d\r\n", d2.i);

    printf("d3.i = %d\r\n", d3->i);

 

    delete d3;

    return 0;

}

 

Output

Enter Demo default ctor. method., objName = Default object.

Enter testMethod1.

Enter Demo(int ival) ctor method,i = 1, objName = d in testMethod1

Exit testMethod1.

Enter Demo copy ctor method.

Enter Demo dector. method, i = 1, objName = d in testMethod1

Enter Demo dector. method, i = 1, objName = copied d

Enter testMethod0.

Enter Demo(int ival) ctor method,i = 0, objName = d in testMethod0

Exit testMethod0.

Enter Demo dector. method, i = 0, objName = d in testMethod0

Enter Demo(int ival) ctor method,i = 999, objName = d1

Enter testMethod2

Enter Demo(int ival) ctor method,i = 2, objName = d in testMethod2

Exit testMethod2.

d.i = 1

d1.i = -2

d2.i = 999

d3.i = 2

Enter Demo dector. method, i = 2, objName = d in testMethod2

Enter Demo dector. method, i = 999, objName = d1

Enter Demo dector. method, i = 1, objName = copied d

 

C# 不同,在 C++ 中,对象声明的时候就已经执行了构造函数,比如上面例子的 main 函数中的第一行, Demo d ,从屏幕上的输出来看,这个时候 Demo class 的默认构造函数会被调用。

接下来的一行代码调用,引出了很有趣的情况,当然也隐藏着不小的问题。这行代码造成了一次构造函数调用,一次拷贝构造函数调用和两次析构函数调用。让我们来具体分析一下:第一次调用构造函数很容易理解,因为在 testMethod1 中我们声明了 Demo d(0) ,退出 testMethod1 ,函数的返回值要赋值给变量 d2 ,这个时候, d2 被拷贝构造函数重新构造了一次。接着 testMethod1 中构造的局部变量被析构,然后,居然拷贝构造函数构造的对象也被析构?等等,看完所有输出,我们发现, objName = copied d 的对象被析构两次,而 objName = Default obj 的对象被构造出之后没有被析构,这里隐藏了很严重的问题,有可能导致内存泄漏、句柄不能被正确关闭等等。另外,拷贝构造函数的执行可能导致潜在的效率问题,考虑一个包含巨大矩阵的对象, copy 这个对象会怎么样?

 

接下来的一行代码, testMethod0 返回一个对象的引用,当然不会导致拷贝构造函数被调用,但是,这样也是有问题的,在函数中声明的局部变量在函数执行完成的时候会被析构,那么直接返回局部变量就可能会出现问题。 testMethod0 退出以后,他内部的 Demo 对象就会自动析构,外面对它的引用当然也无法指向正确的对象了,所以后面程序打印 d.i 的时候,输出了一个莫名其妙的 -2

 

效率最好的方法当数返回指针了,它不会导致对象复制,如果使用得当,也不会导致内存泄漏或者句柄泄漏。 testMethod2 演示了这种情况,当然,你需要手工删除在 testMethod2 中创建的对象。

 

 

Feedback

# re: C++对象的构造、赋值和析构  回复  更多评论   

2006-11-28 09:50 by iceboundrock
补充一点关于数组的内容,
如果在C++中声明一个对象的数组,不像C#,数组中默认的元素都是null,C++的数组中可以容纳多少元素就会在声明数组的时候执行多少次构造函数将实际函数构造出来。

只有注册用户登录后才能发表评论。


网站导航:
 

posts - 10, comments - 15, trackbacks - 0, articles - 0

Copyright © iceboundrock