首页 > 代码库 > 详谈c++的构造和析构
详谈c++的构造和析构
c++的构造
概念:对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作
例:
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A():a(1),b(0){cout<<"A"<<endl;} 7 private: 8 int a; 9 const int b; 10 }; 11 12 int main() 13 { 14 A a; 15 A*b(&a); 16 A &c(a); 17 return 0; 18 }
A *(&a) 和 A &c(a)都没有产生新的对象,所以都没有在调用构造函数,输出结果为:A
可通过构造函数对成员变量进行初始化
子父类的构造与析构
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A(){cout<<"A"<<endl;} 7 virtual ~A(){cout << "~A" <<endl;} 8 }; 9 10 class B{ 11 public: 12 B(){cout << "B" <<endl;} 13 virtual ~B(){cout << "~B" << endl;} 14 }; 15 16 class C : public A{ 17 public: 18 C(){cout << "C" << endl;} 19 virtual ~C(){cout << "~C" <<endl;} 20 private: 21 B b; 22 }; 23 24 int main() 25 { 26 C c; 27 return 0; 28 }
输出结果为:
1 A 2 B 3 C 4 ~C 5 ~B 6 ~A
父类的构造函数:
(1)如果某个类具有基类,执行基类的默认构造函数。除非在ctor-initializer中调用了基类构造函数,此时调用这个构造函数,而不是默认构造函数
(2)类的非静态数据成员按照声明的顺序创建
(3)执行该类的构造函数
父类的析勾函数:
(1)调用类的析构函数
(2)销毁类的数据成员,与创建顺序相反
(3)如果有父类,调用父类的析构函数
注意所有的析构函数都是virtual。如果前面的析构函数没有声明为virtual,代码也可以继续运行。然而,如果代码使用delete删除一个实际指向派生类的基类指针,析构函数调用链将被破坏。如下:
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A(){cout<<"A"<<endl;} 7 ~A(){cout << "~A" <<endl;} 8 }; 9 10 11 class C : public A{ 12 public: 13 C(){cout << "C" << endl;} 14 ~C(){cout << "~C" <<endl;} 15 }; 16 17 int main() 18 { 19 A *c = new C();z 20 delete c; 21 return 0; 22 }
输出结构为:
A C ~A
注:当基类代码中显示了没有默认构造函数,相关版本的派生类必须显示地告诉编译器如何调用Super的构造函数,否则代码将无法编译。
1 class Super 2 { 3 public: 4 Super(int i); 5 }; 6 class Sub : public Super 7 { 8 public: 9 Sub(int i); 10 }; 11 12 Sub::Sub(int i) : Super(i) 13 { 14 15 }
从派生类向基类传递构造函数的参数很正常,毫无问题,但是无法传递数据成员。因为在调用基类的构造之后才会初始化数据成员。如果将数据成员作为参数传递给父类构造函数,数据成员不会初始化。
父类的析构函数
析构函数的调用顺序刚好与构造函数相反:
(1)调用类的析构函数
(2)销毁类的数据成员,与创建的顺序相反
(3)如果有父类,调用父类的析构函数
注意:所有的析构函数都是virtual。根据经验,所有析构函数都应该声明为virtual。如果前面的析构函数没有声明为virtual,代码也可以继续运行。然而,如果代码使用delete删除一个实际指向派生类的基类指针,析构函数调用链将被破坏。
从技术角度看,将基类的析构函数声明为virtual,可以纠正上面的问题。派生类将自动“虚化”。然而,建议显示地将所有析构函数声明为virtual
详谈c++的构造和析构