02 C++类基础(1)

1 虚析构函数的意义?

意义:通过基类指针删除派生类对象时并不会调用派生类所对应的析构函数,而是调用基类中的析构函数,这与我们的初衷相悖。

解决:将析构函数定义为虚函数,这样基类指针删除派生类对象先调用派生类的析构函数,再调用基类的析构函数。

定义虚析构函数的场景:一个类中定义了虚函数。一个类作为基类使用。

注意点:不允许以虚函数作为构造函数

2 多态的意义?

多态的场景

  • 派生类对象的地址赋值给基类类型指针
  • 派生类的对象可以赋给基类对象引用

多态的概念辨析?

  • 在非析构或构造函数中调用虚函数,也是多态
  • 在析构函数与构造函数中调用虚函数(编译时即确定)不是多态。

多态的实现方式?

​ 多态中基类指针或引用调用虚函数时在编译的过程中是无法判断调用哪个类中的虚函数的。在运行时才确定调用哪个虚函数。这被称之为动态联编。 动态联编实现方法? 通过虚函数表来实现,每一个有虚函数的类,该类的任何对象中都存放着一个虚函数表的指针,该指针即存放着虚函数表的地址。

多态机制中虚函数调用与访问权限关系

  • 如果基类将虚函数定义为private,派生类无法调用。
#include <iostream>
using namespace std;
class Base{
    public:
        virtual void fun2() {
            cout << "Base: Fun2 is called" << endl;
        }
};
class Derived:public Base{
    private:
        virtual void fun2(){
            cout << "Base:Fun 2 is called" << endl;
        }    
};
/*    Base        Derived                      Complier error
      public      private                          No
      private     public or private                Yes
      public      public                           No
多态机制中能否调用子类的虚函数取决与基类中该虚函数的访问权限
*/
int main()        
{
    Derived d;
    Base * p;
    p = &d;    
    p->fun2();
    return 0;    
}    

3 派生类与基类的关系?

  • 派生类中可以扩充新的成员函数与成员变量,也可重新定义基类中的成员函数。
  • 派生类已经定义后可独立使用,不依赖于基类。派生类拥有基类的全部(成员变量与成员函数),但
  • 派类的成员函数无法访问基类的private.
  • 派生类对象包含基类对象,其大小等于基类对象大小加上新定义的成员函数与变量,并且基类对象存储位置在派生类成员之前。

3-1 派生类与基类创建的先后关系?

  • 在执行派生类的构造函数之前总是先执行基类的构造函数。
  • 派生类的析构函数被执行后自动调用基类的构造函数。

3-2 派生类中的覆盖现象?

  • 在派生类中定义与基类同名的成员,当对象访问该成员时,默认访问派生类所定义的成员,如果要访问基类中同名成员,需要加上基类的作用域。注意:实际编程中,一般不会在派生类定义与基类同名的成员变量,同名成员函数可以定义。

3-3 this指针的常识?

  • 假如一个class A定义了一个对象,那么this指针就是指向该对象。非静态成员函数可以直接使用this代替指向该函数作用的对象的指针

3-4 封闭类以及其特点?

成员对象的类叫做封闭类。

特点:

  • 封闭类必须保证在其中的成员对象能够初始化
  • 通过其构造函数的初始化列表进行初始化。封闭类对象生成时,先调用其成员对象的构造函数,再调用封闭类的构造函数。
  • 封闭类析构时,先调用封闭类的析构函数,再调用各成员对象的析构函数

4 const修饰变量、函数、对象?

变量:无法进行修改

函数:函数内的变量无法修改

对象:只能调用析构函数,构造函数以及常量成员函数。

5 如何计算C++类占用空间大小?

C++:

  • 类中的计算只考虑成员变量(注意内存对齐),静态成员与成员函数不考虑。

  • 含有虚函数的类或实例的大小 = 成员变量+虚函数表指针的大小(8个字节)

内存对齐的规则:数据起始位置只能设置成1,2,4,8,16.

关于虚函数表的常识:

  • 定义虚函数,或者继承了带有虚函数的类都会有虚函数表,在存储上体现为表的指针。
  • 派生类覆写基类虚函数方法会在虚函数表中覆写来自于父类的虚函数方法
  • C++中单继承有虚函数的基类则只有一个虚函数表,继承n个带有虚函数的基类,则有n个虚函数表

实例

#include<iostream>
using namespace std;
class A          // 1个字节 = 空的类
{     
};    

class B         // 16个字节 = 虚函数表指针:8+内存对齐后char:8
{                                     
    char ch;      
    virtual void func0()  {  }   
};   
 
class C         // 虚函数表指针:8 + 内存对齐的2个char:8 = 16字节
{  
    char ch1;  
    char ch2;  
    virtual void func()  {  }    
    virtual void func1()  {  }   
};  

class D: public A, public C    // 虚函数表指针(8)+父类的2个char+int+内存对齐(8) = 16字节
{     
    int d;     
    virtual void func()  {  }   
    virtual void func1()  {  }  
};     
// 情况5:多重继承,含虚函数覆盖的情况
class E: public B, public C    // 2个父类的虚函数表指针(16)+父类的3个char(8)+int(8) = 32字节
{     
    int e;     
    virtual void func0()  {  }   
    virtual void func1()  {  }  
};  

执行结果

A=1
B=16
C=16
D=16
E=32

参考资料

01 《新标准C++程序设计》 郭炜 编著

02 MOOC 程序设计与算法(三)

原文地址:https://www.cnblogs.com/kfcuj/p/14778832.html