Chinaunix首页 | 论坛 | 博客
  • 博客访问: 151642
  • 博文数量: 36
  • 博客积分: 802
  • 博客等级: 准尉
  • 技术积分: 717
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-02 22:47
文章分类
文章存档

2012年(36)

分类: C/C++

2012-10-11 21:45:38

问题一:复制构造函数
参数的传递方式有两种:传值调用和传址调用。
传值调用的时候,传送的是作为实际参数的对象的副本而不是实际对象本身
  1. #include<iostream>
  2. using namespace std;

  3.  class square
  4. {
  5.     int side;
  6.     public:
  7.     square(int x)
  8.     {
  9.         side=x;
  10.         cout<<"constructiong\n";
  11.     }
  12.     ~square()
  13.     {
  14.         cout<<"Destructiong"<<"\n";
  15.     }
  16.     void display()
  17.     {
  18.         cout<<side<<"\n";
  19.     }
  20. };

  21. void f(square ob)
  22. {
  23.     ob.display();
  24. }
  25. int main()
  26. {
  27.     square s(10);
  28.     f(s);//对象s以传值方式给临时对象x
  29.     s.display();
  30.     return 0;

  31. }
输出结果:
Constructiong
10
Destructiong
10
Destructiong
为什么构造函数执行一次,而析构函数执行两次呢?
当一个对象被作为参数传递给函数的时候,同时也创建了该对象的副本。(这个副本才是函数的参数)这就是说:创建了一个新的对象。当函数结束的时候,作为函数的实际参数的副本也将被销毁,这产生的问题有:
(1)在创建对象的副本的时候是否调用了构造函数?
(2)在销毁对象的副本的时候是否调用了析构函数?

首先,在调用函数的时候,程序创建了一个对象的副本作为形式参数,此时普通的构造函数并没有调用。而是调用了复制构造函数。如上代码所示:创建形式参数ob的时候,是通过square类的复制构造函数进行传参数的,并没有调用用来执行初始化的普通的构造函数square(int x)
复制构造函数定义了如何创建一个对象的副本。
如果一个类中没有显示的定义类的复制构造函数,那么C—++将提供一个默认的复制构造函数。(默认的复制构造函数将以按位复制的形式创建了一个对象的副本)
由于普通的构造函数通常用于初始化对象的某些成员,因此就不能调用普通构造函数创建对象的副本,因为这样产生的对象可能与现在的对象不完全相同。当把一个对象传递给函数的时候,需要使用的是对象的当前状态,而不是初始。
其次,
当函数结束的时候, 由于作为参数的对象副本超出了作用域。因此它将被销毁!!从而调用析构函数,这就是为什么前面的程序中调用两次:第一次是因为函数display()的参数超出了作用域,第二次才是因为住函数main()中的对象s在程序结束时候,被销毁。
综上所述:当创建一个对象的副本作为函数的参数的时候,普通的构造函数没有被调用,锁调用的构造函数是按位复制的默认复制构造函数,但是当对象的副本被销毁的时候,常常因为(函数返回而超出作用域),析够函数被调用。
自定义的复制构造函数:
当使用一个对象来初始化另一个对象的时候,也将调用复制构造函数。
eg:什么是自定义的复制构造函数?


  1. #include<stdlib.h>
  2. #include<string.h>
  3. #include<iostream>
  4. using namespace std;

  5. class my_string
  6. {
  7.     char *s;
  8. public:
  9.     my_string(char *str);//构造函数
  10.     my_string(const my_string &obj);//自定义的复制构造函数
  11.     ~my_string()
  12.     {
  13.         if(s)
  14.             delete [] s;
  15.         cout<<"Freeing s\n";
  16.     }
  17.     void show()
  18.     {
  19.         cout<<s<<"\n";
  20.     }
  21. };
  22. my_string::my_string(char *str)
  23. {
  24.     s=new char[strlen(str)+1];
  25.     cout<<"Allocating room for s\n";
  26.     strcpy(s,str);
  27. }

  28. my_string::my_string(const my_string &obj)
  29. {
  30.     s=new char[strlen(obj.s)+1];
  31.     strcpy(s,obj.s);
  32.     cout<<"Copy constructor called.\n";
  33. }
  34. void display(my_string ob)
  35. {
  36.     ob.show();
  37. }

  38. int main()
  39. {
  40.     my_string obj("Hello!");
  41.     display(obj);
  42.     obj.show();
  43.     return 0;
  44. }
本程序在运行时候发生的动作:在函数main()中创建对象obj的时候,普通构造函数为对象obj分配了动态内存并且将内存地址赋给了变量obj.s。接下来,对象obj被作为参数传递给display()中的ob。当这个动作发生的时候,对象obj的复制构造函数被调用,从而创建了对象obj的一个副本。
当函数display()结束时候,对象副本被销毁,此时将调用析构函数,析构函数会释放ob.s所指的动态内存空间。不会影响对象obj。

复制构造函数只有在初始化对象的时候才被调用。例如:
  1. my_string s1("Hello"),s2("world");
  2. s1=s2;
  3. 则在上面的代码中:s1=s2执行的是赋值运算,而不是初始化
复制构造函数与初始化:
当使用一个对象来初始化另一个对象的时候,也将调用复制构造函数。
  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;

  4. class my_string
  5. {
  6.     char *s;
  7. public:
  8.     my_string(char *str);//普通构造函数
  9.     my_string(const my_string &obj);//自定义的复制构造函数
  10.     ~my_string(){
  11.         if(s)
  12.             delete [] s;
  13.         cout<<"Freeing s\n";
  14.     }
  15.     void show()
  16.     {
  17.         cout<<s<<"\n";
  18.     }
  19. };

  20. my_string::my_string(char *str)
  21. {
  22.     s=new char[strlen(str)+1];
  23.     cout<<"Normal constructor called.\n";
  24.     strcpy(s,str);
  25. }

  26. my_string::my_string(const my_string &obj)
  27. {
  28.     s=new char[strlen(obj.s)+1];
  29.     strcpy(s,obj.s);
  30.     cout<<"copy constructor called.\n";
  31. }

  32. int main()
  33. {
  34.     my_string obj("Hello!");//调用构造函数
  35.     my_string ob1(obj);//调用复制构造函数
  36.     my_string ob2=ob1;//调用复制构造函数

  37.     ob1.show();
  38.     ob2.show();

  39.     return 0;
  40. }
复制构造函数在返回时使用复制构造函数
函数创建了一个临时对象保存要返回的值,而函数锁返回的对象实际上是这个临时对象,在对象的值被返回之后,临时对象将被销毁。(在某种情况下会引发不可预料的后果)
例如:
  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;

  4. class my_string
  5. {
  6.     char *s;
  7. public:
  8.     my_string(char *str);//普通构造函数
  9. //    my_string(const my_string &obj);//自定义的复制构造函数
  10.     ~my_string(){
  11.         if(s)
  12.             delete [] s;
  13.         cout<<"Freeing s\n";
  14.     }
  15.     void show()
  16.     {
  17.         cout<<s<<"\n";
  18.     }
  19. };

  20. my_string::my_string(char *str)
  21. {
  22.     s=new char[strlen(str)+1];
  23.     cout<<"Normal constructor called.\n";
  24.     strcpy(s,str);
  25. }
  26. /*
  27. my_string::my_string(const my_string &obj)
  28. {
  29.     s=new char[strlen(obj.s)+1];
  30.     strcpy(s,obj.s);
  31.     cout<<"copy constructor called.\n";
  32. }
  33. */
  34. my_string input()//返回一个my_stirng类型的对象
  35. {
  36.     char instr[80];
  37.     cout<<"Enter a string:";
  38.     cin>>instr;
  39.     my_string ob(instr);//调用普通构造函数
  40.     return ob;
  41. }
  42. int main()
  43. {
  44.     my_string obj=input();//调用默认的复制构造函数
  45.     //在函数input中调用普通的构造函数
  46.     obj.show();
  47.     return 0;
  48. }
错误原因:当对象从函数input()返回的时候,在函数中自动产生一个临时对象(调用默认的复制构造函数完成),它保存了要返回的值。在这种情况下,临时对象中的值只是对象ob中值的按位复制副本。当函数input()返回的时候,临时对象将被销毁,此时将调用析构函数。因为临时对象中用来保存用户输入的字符串的动态内存已经被释放了,所以在main()中调用函数show()时候输出的将是垃圾数据。
----》避免出现错误的一个方法是返回一个对象指针或者对象引用
另一种方法就是使用自定义的复制构造函数。
  1. #include
  2. #include
  3. using namespace std;

  4. class my_string
  5. {
  6. char *s;
  7. public:
  8. my_string(char *str);//普通构造函数
  9. my_string(const my_string &obj);//自定义的复制构造函数
  10. ~my_string(){
  11. if(s)
  12. delete [] s;
  13. cout<<"Freeing s\n";
  14. }
  15. void show()
  16. {
  17. cout<
  18. }
  19. };

  20. my_string::my_string(char *str)
  21. {
  22. s=new char[strlen(str)+1];
  23. cout<<"Normal constructor called.\n";
  24. strcpy(s,str);
  25. }

  26. my_string::my_string(const my_string &obj)
  27. {
  28. s=new char[strlen(obj.s)+1];
  29. strcpy(s,obj.s);
  30. cout<<"copy constructor called.\n";
  31. }
  32. my_string input()
  33. {
  34. char instr[80];
  35. cout<<"Enter a string:";
  36. cin>>instr;
  37. my_string ob(instr);
  38. return ob;
  39. }
  40. int main()
  41. {
  42. my_string obj=input();//调用复制构造函数
  43. //在函数input()中调用普通构造函数
  44. obj.show();
  45. return 0;
  46. }
这样就解决了问题。当函数返回对象的时候,会调用复制构造函数来创建临时对象
阅读(1854) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~