之前的笔记搬运
虚函数补充
虚表的函数排序默认是按定义顺序, 但是有函数重载的情况下,顺序可能会有变化;
构造函数无法定义成虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。
析构函数可以定义成虚函数, 并且以后写类时, 析构函数一定要定义成虚函数,
否则可能会造成内存泄露;
如:
1 | 定义了两个类 A和B, 并且B继承了A, 如果实例化B的对象给A类的指针, |
那么释放时就会用delete a, 这时候就会出现只调用A类的析构函数,
而B的内存空间无法释放, 所以会造成内存泄露, 而把A和B的析构函数都定义成虚函数,
那么这时候析构函数也会加入虚表,
并且a和b虚表内的析构函数指针都会指向b的析构函数, 就可以避免这个问题;
如:
1 | class A{ virtual \~A(){}; };//析构定义成虚函数 |
析构函数定义成虚函数还有一个问题, 那就是 显示调用析构函数的话, 会影响影响虚表,
导致多态性失效;
如: 定义了两个类 A和B, 并且B继承了A, 并且定义个A指针, 创建一个B类把地址赋值给A,
如果在中途调用了A的析构函数, 就会破坏虚表, 导致下面无法调用B的所有虚函数;
1 | A \*pA = new B(); |
重载, 覆盖, 隐藏的构成条件:
1 | 1.重载: |
1 | 2.覆盖 |
1 | 3.隐藏 |
纯虚函数
如果一个至少有一个纯虚函数,那么这个就是一个抽象类
1 | 抽象类: |
1 | 作用: |
需要注意的地方:
类的析构函数也可以定义成纯虚函数, 但是格式不同;
纯虚析构函数定义格式: virtual ~类名() = 0 {}
不可以写成:virtual ~类名() = 0;
禁用隐式转换:
带参构造函数只有一个参数时候, 可以这么写:
1 | 类名 对象名( 参数 ); |
使用第二种方式进行实例化对象时, 其实编译器是偷偷进行了隐式转换的;
构造函数使用关键字: explicit 可以关闭隐式转换;
如定义类时写构造函数: explicit 类名(参数) {}
那么就不可以使用第二种方式进行实例化对象对象了
类型转换检查:
在类成员函数指针类型转换过程中, 派生类转基类指针, 会自动兼容的,
但是派生类指针强转基类指针就会有安全隐患;