类临时变量的生存期(转)

临时变量的生存期

标签: destructorconstructorclassctimer编译器
分类:

在项目中碰到一个问题,在某个timer的构造函数中构造了一个对数据库的connection,但是在构造函数还没结束的时候connection就关闭了。后来才知道是因为临时变量的生存期的问题。大概的意思如下:

[cpp] view plain copy
 
  1. #include <iostream>  
  2. using namespace std;  
  3. class A {  
  4.     public:  
  5.         A():a(0) {  
  6.             cout << "A :: default constructor " << endl;  
  7.         }  
  8.         A(int x):a(x){  
  9.             cout << "A :: constructor " << endl;  
  10.         }  
  11.         A& operator=(const A& r) {  
  12.             cout << "A :: operator = " << endl;  
  13.             a = r.a;  
  14.             return *this;  
  15.         }  
  16.         ~A() {  
  17.             cout << "A :: destructor " << endl;  
  18.         }  
  19.     private:  
  20.         int a;  
  21. };  
  22.   
  23. class C {  
  24.     public:  
  25.         C():i(0) {  
  26.             cout << "C :: default constructor " << endl;  
  27.         }  
  28.         ~C() {  
  29.             cout << "C :: destructor " << endl;  
  30.         }  
  31.     private:  
  32.         int i;  
  33. };  
  34.   
  35. class B {  
  36.     public:  
  37.         B() {  
  38.             this->ba = A(1);  
  39.             cout << "B :: default constructor " << endl;  
  40.         }  
  41.         ~B() {  
  42.             cout << "B :: destructor " << endl;  
  43.         }  
  44.     private:  
  45.         A ba;  
  46.         C bc;  
  47. };  
  48.   
  49. int main() {  
  50.     B *pB = new B;  
  51.     delete pB;  
  52.     return 0;  
  53. }  
输出的结果为:
A :: default constructor
C :: default constructor
A :: constructor
A :: operator =
A :: destructor
B :: default constructor
B :: destructor
C :: destructor
A :: destructor

我们一行一行的分析,首先new了一个B的对象出来,B中有两个成员,分别是A类型和C类型,于是自动调用A类和C类的默认构造函数进行初始化,于是输出前两行。
然后在B的构造函数里面构造了一个A的临时对象,并且付给ba,于是输出第3,4行。但是这个临时变量的生存期只有一行,所以输出第5行。接着输出第6行。
最后delete掉这块空间,先调用B的析构,再调用成员的析构,于是输出7,8,9行。

这里的关键就是临时变量的生存期只有一行,这样就能解释在文章开头说的还没有出构造函数,connection就被关掉了,因为关掉了临时对象的连接,也就关掉了timer类中conn成员的连接。

----------------------------------------------------------------------------------------------

在刚才写的时候还碰到了这么一个问题,类C是我新加入的一个类,本来的代码是这样的:
[cpp] view plain copy
 
  1. #include <iostream>  
  2. using namespace std;  
  3. class A {  
  4.     public:  
  5.         A():a(0) {  
  6.             cout << "A :: default constructor " << endl;  
  7.         }  
  8.         A(int x):a(x){  
  9.             cout << "A :: constructor " << endl;  
  10.         }  
  11.         A& operator=(const A& r) {  
  12.             cout << "A :: operator = " << endl;  
  13.             a = r.a;  
  14.             return *this;  
  15.         }  
  16.         ~A() {  
  17.             cout << "A :: destructor " << endl;  
  18.         }  
  19.     private:  
  20.         int a;  
  21. };  
  22.   
  23. class C;  
  24.   
  25. class B {  
  26.     public:  
  27.         B() {  
  28.             this->ba = A(1);  
  29.             cout << "B :: default constructor " << endl;  
  30.         }  
  31.         ~B() {  
  32.             cout << "B :: destructor " << endl;  
  33.         }  
  34.     private:  
  35.         A ba;  
  36.         C bc;  
  37. };  
  38.   
  39. class C {  
  40.     public:  
  41.         C():i(0) {  
  42.             cout << "C :: default constructor " << endl;  
  43.         }  
  44.         ~C() {  
  45.             cout << "C :: destructor " << endl;  
  46.         }  
  47.     private:  
  48.         int i;  
  49. };  
  50.   
  51. int main() {  
  52.     B *pB = new B;  
  53.     delete pB;  
  54.     return 0;  
  55. }  
这时候编译会产生错误,错误在第36行,说bc是一个不完全类型。
这是因为在第23行的声明是一个前向声明(forward declaration)。在类C的声明之后,定义之前,类C都是一个不完全类型(incomplete type)。即已知C是一个类型,但是不知道它包含哪些成员。
在创建类的对象之前,必须完整的定义类,不能仅仅声明类。这样,编译器才会为类的对象预定空间。同样的,在使用指针或者引用访问类成员之前,必须定义该类。例如:
[cpp] view plain copy
 
  1. #include <iostream>  
  2. using namespace std;  
  3. class A {  
  4.     public:  
  5.         A():a(0) {  
  6.             cout << "A :: default constructor " << endl;  
  7.         }  
  8.         A(int x):a(x){  
  9.             cout << "A :: constructor " << endl;  
  10.         }  
  11.         A& operator=(const A& r) {  
  12.             cout << "A :: operator = " << endl;  
  13.             a = r.a;  
  14.             return *this;  
  15.         }  
  16.         ~A() {  
  17.             cout << "A :: destructor " << endl;  
  18.         }  
  19.     private:  
  20.         int a;  
  21. };  
  22.   
  23. class C;  
  24.   
  25. class B {  
  26.     public:  
  27.         B() {  
  28.             this->ba = A(1);  
  29.             cout << "B :: default constructor " << endl;  
  30.             //bc->func();   //error  
  31.         }  
  32.         ~B() {  
  33.             cout << "B :: destructor " << endl;  
  34.         }  
  35.     private:  
  36.         A ba;  
  37.         C* bc;  
  38. };  
  39.   
  40. class C {  
  41.     public:  
  42.         C():i(0) {  
  43.             cout << "C :: default constructor " << endl;  
  44.         }  
  45.         ~C() {  
  46.             cout << "C :: destructor " << endl;  
  47.         }  
  48.           
  49.         void func() {  
  50.             cout << "C :: func()" << endl;  
  51.         }  
  52.     private:  
  53.         int i;  
  54. };  
  55.   
  56. int main() {  
  57.     B *pB = new B;  
  58.     delete pB;  
  59.     return 0;  
  60. }  
在第30会报错,因为C这时候是一个不完全类型,而37行不会报错。
只有当类的定义在前面出现过,数据成员才能被指定为该类类型。如果该类型是不完全类型,那么数据成员只能是指向该类型的指针或者引用。
综合来说,就是:如果C是一个不完全类型,那么可以定义C类型的指针和引用(引用主要用于形参),如上边第37行。但是不能访问类C的成员,如第30行。
只有当类的定义体完成后才能定义类,因为一个类中不能含有自身类型的对象。然而,只要类名一出现就被认为是已经声明过,所以可以在类中定义自身类型的指针或者引用。
比如:
[cpp] view plain copy
 
  1. class A {  
  2. private:  
  3.     A *prev;  
  4.     A *next;  
  5. };  

 

posted on 2016-01-29 17:45  hustfeiji  阅读(160)  评论(0)    收藏  举报

导航