C++构造函数、析构函数、虚函数

来源:互联网 发布:淘宝服装模特拍摄价格 编辑:程序博客网 时间:2024/06/04 19:44

今天看书时发现对c++虚函数、构造函数、析构函数的一些概念竟然极其不清晰。

查了一些书,总结一下。

构造函数

1.创建对象时会依次调用基类和子类的构造函数,各个构造函数负责对自己类中定义的成员的初始化工作。

2.如果用户不声明任何构造函数,编译器将提供一个缺省构造函数(default constructor),只要用户定义了自己的构造函数,不论包不包括缺省构造函数,编译器都不再提供缺省构造函数,因此如果你不希望以不含参数的方式构造该类对象,那么可以给出你希望的构造函数而不给出自定义的缺省构造函数,而如果你没有这种要求而这样做的话,那么你如果用不含参数的方式构造该类对象则会得到一个编译器错误,因为编译器找不到合适的构造函数来完成这个动作。

3.有3个函数是编译器会自动为你编写的,它们是拷贝构造函数,赋值操作符和析构函数。拷贝构造函数和赋值操作符作用类似,前者用一个对象构造一个新对象,后者用一个对象给另一个对象赋值,两者还是有所区别的。

编译器提供的拷贝构造函数和赋值操作符都是执行的“浅拷贝”,即对于类中的所有数据成员来说:如果是用户自定义类型,就调用该类的拷贝构造函数;如果是内置类型,则使用“复制所有bits”的方式实现拷贝,这种浅拷贝对于指针成员会有问题,因为它会造成两个指针指向一个内存地址的情况,这显然是不正确的,为了避免这种情况,就应该对那些有指针成员的类自定义提供拷贝构造函数和赋值操作符,在实现中动态分配新的内存,这就是所谓的“深拷贝”。

拷贝构造函数和赋值操作符的不同体现在这个地方:因为前者进行的是初始化,而后者进行的是赋值,而对于引用对象和const对象是不可以进行赋值的,因此C++不允许对含有引用对象和const对象的类调用赋值操作符,否则会给出一个编译错误。

析构函数

与构造函数可以有多个版本不同,析构函数只有一个版本,因为它不需要任何参数。

销毁对象的时候按照与构造函数正好相反的顺序依次调用子类和基类的析构函数。一般情况下用户不需要自定义析构函数,编译器提供的构造函数就足够了(其实它什么都不做),因为除了动态分配的内存外,其余数据成员都会在该对象生命周期结束时自动被销毁。正如刚才所说,当对象中含有动态分配内存的数据成员时,就需要自定义析构函数来调用delete来释放那些内存了。

对于基类来说,将析构函数定义为virtual是适合的,因为基类指针或引用可能被用来指向子类对象,而如果通过该指针或引用来调用delete来析构该对象的话,如果基类的析构函数不是virtual,那么这种行为是c++没有定义的,通常会调用基类的析构函数(而合适的做法显然是调用子类,也就是该对象实际类型的析构函数),这样的结果会是这个对象的基类部分被析构而子类部分没有得到析构。

虚函数

C++使用虚函数机制来实现多态。一个函数从它被声明为virtual的那一层基类起就永远是virtual的,不管子类是否覆盖它,也不管子类在覆盖它的时候是否声明为virtual。只有通过基类指针或者引用调用虚函数的时候才表现出多态(也就是运行时决定调用哪个函数),除此之外,如果用基类对象调用当然会调用基类函数,如果用子类指针、引用或对象调用都会调用子类函数(如果覆盖了的话,如果没覆盖当然调用基类函数)。

C++通过vptr和vtbl来实现运行时多态。任何有至少一个virtual函数的类,其对象对比那些正常类的对象都多一个数据成员,就是vptr,这是一个指向vtbl的指针,而vtbl(虚函数表,包含该类所有虚函数对应的函数地址)只需要由整个类维护一个就好了。编译器在编译的时候对于有多态效应的函数调用(即由基类指针或引用调用的虚函数)用该函数在vtbl中的下标代替(猜测:基类的vtbl和子类vtbl的前半部分相同,这样下标才能一致),然后在运行时动态构造对象时,可以根据该对象中的vptr找到正确的vtbl(即该对象真实类别对应的虚函数表),再根据下标找到正确的虚函数。

原创粉丝点击