子类对象可以自由使用父类的public成员
类型转换:
1.从派生类向基类的转换只对指针或引用类型有效。
2.基类向派生类不存在隐式转换
3.通常能够将一个 派生类对象拷贝、移动、或赋值给一个基类对象,但是这种操作只处理派生类对象的基类部分,派生类部分会被切掉,被丢弃。
4.对于代码中的某个给定结点来说,如果基类的公有成员是可访问的,则派生类向基类的类型转换也是可访问的。
基础知识:
1.构造一个派生类对象时,先用基类的构造函数来构造基类部分,再调用派生类的构造函数来构造派生类部分,派生类构造函数只初始化它的直接基类。
2.静态成员都是公有的,不属于任何一个单独的对象,可以通过对象调用它,也可以通过作用域运算符调用它。
3.一个类被继承之前必须已经定义而非仅仅声明。声明一个类时,不可以包含派生列表。
4.当final在类名后面时,表示该类不能被继承;当final在一个函数声明末尾时,表示该函数不能被重写。
5.当派生类定义了拷贝或移动操作时,该操作负责拷贝或移动包括基类部分成员在内的整个对象。
继承限定词:public, protected、private
1.类型限定词public不改变父类成员的访问权限,父类怎样,子类就怎样;类型限定词public不改变父类成员的访问权限,父类怎样,子类就怎样;类型限定词protected类型限定词,降低父类成员公有化程度,提高私有化程度,public变protected,其他不变;private更加降低父类成员公有化程度,提高私有化程度,public降成private,全变私有
2.派生类的成员和友元只能访问派生类对象中的基类部分的受保护成员。
3.在子类成员函数内部,无论类型限定词是什么,子类成员函数都能且只能父类的public和protected成员,而不能调用父类的private成员
若是子类使用的是class关键字,但是没有显示指定继承限定词,则默认为private。
若是子类使用的是struct关键字,但是没有显示指定继承限定词,则默认为public。
继承构造调用顺序:
1.父类的构造函数是无参数的:先调用父类的构造函数,再调用子类的构造函数
2.父类的构造函数是有参数的:必须显示的写出子类的构造函数,且在构造函数之后加冒号:和父类类名和需传递的实参 如:son() : Cfather(1) { } ; Cfather(int a) : Cgrandfather(1, 2){ }
有多个构造函数时,通过参数列表的个数来表明所调用的构造函数
继承析构函数的调用顺序与构造函数相反,先调用子类的析构函数,再调用父类的析构函数
覆盖:
1.子类和父类数据成员相同时,子类对象调用该数据成员则是调用子类的数据成员,若要其调用的是父类的那个同名数据成员,则应该在数据成员前加上父类的作用域,如:s.Cfather::a,若没有显示作用域的指定,则默认调用子类成员,这个现象称之为覆盖,子类的数据成员
2.父类和子类的成员函数相同时,不管他们的参数列表相不相同,只要函数名相同,则会被覆盖,需加上父类作用域运算符和相应参数列表。如:s.Cfather::fun(1);,PS :覆盖不是重载,想要调用父类成员函数必须加上父类作用域运算符。
3.父类的友元函数不能被继承,父类的友元只能访问父类的私有成员,而不能访问子类的私有成员,子类的友元只能访问子类的私有成员。
4.基类友元可以访问派生类对象中基类部分。
虚函数:
多态:父类的指针调用子类的函数
父类的指针,同样的调用有多样的状态,即多态
father *fa = new son1(); //创建一个父类的指针指向子类的空间 PS:以往是一个指针只能指向同类型的空间
fa->show() ; //fa只能调用父类的成员,一个类型的指针只能调用该类类型的成员
//在父类同名成员类型前加上关键字virtual,则父类指针调用的是所指空间的子类的成员
虚函数的特点:重写就是普通函数的覆盖
1.子类重写父类后,子类的那个函数也变成了虚函数,隐式的带有virtual关键字(为了读程序方便,代码可读性更高,可以显示加上virtual关键字)
2.重写关系条件:函数的名字,参数列表、和返回值必须相同,函数体不必相同。返回值不同会报错,参数列表和函数名不同则是调用父类成员,忽略virtual关键字。基类和派生类的函数名相同,但参数列表不同,则在派生类作用域中,派生类会将之隐藏。
2.返回值不同有一种特殊情况:当父类和子类的虚函数都返回类本身的引用或指针时,这种情况称之为协变,可以编译通过,可构成虚函数。
3.virtual关键字会忽略inline内联关键字,所以内联关键字会失效。
4.构造函数因为是内联函数,所以不能是虚函数,并且子类和父类的构造函数肯定不同名。且除构造函数之外,任何成员函数都可以生命为虚函数。
5.如果基类把一个函数声明成虚函数,则无论它是否在类中被覆盖,都是虚函数。
6.派生类的成员将隐藏同名的基类成员。
虚表:存放虚函数地址的列表
将带有virtual关键字的父类成员函数地址都存入一个虚函数列表,若是子类在遇到某个同名的函数,则用子类函数的地址去重写原函数的地址,也就是覆盖,等下次调用时直接调用这些地址就能调用相应函数。
主函数写错了,主函数过多或没写,项目检错了,头文件不能有主函数
((p)*((int*)*(int *)fa))();
析构函数可以是虚函数
1指针是什么类型,dete就调用谁的析构函数,但是本应该是内存是什么类型就应该调用什么类型的析构函数。,若基类的析构函数不是虚函数,则delete 一个指向派生类对象的基类指针将产生未定义的行为,因为delete该指针只是释放了对象内存中基类部分的内存。
2.只有将父类的析构函数声明为virtual的,最终才能同时调用子类和父类的析构函数,从而将整块内存释放。否则只会调用父类的析构函数。
3.基类通常都应该定义一个虚析构函数。
4.派生类的析构函数除了要销毁自身的成员,还要通过调用基类的析构函数来销毁直接基类的成员。
纯虚函数:在一个函数声明末尾加上=0的函数。
1.不能实例化对象
2.除非用一个子类去继承这个基类,并将这个纯虚函数实现,最终就可以用子类来定义对象,但父类还是不能实例化对象。若是子类也未将该纯虚函数重写,则子类也不能实例化对象。
3.含有纯虚函数的类就是抽象类
4.成员函数全是纯虚函数的类就叫接口类。(可以有数据成员和构造函数)
虚继承:解决多继承中的访问不明确问题
在继承限定词前加virtual关键字