继承有三种继承方式,public继承、protected继承、private继承
基类成员的访问权限在派生类里必须是 <= 继承方式的
继承方式 | 基类的public成员 | 基类的protected成员 | 基类的private成员 |
public继承 | 仍是public成员 | 仍是protected成员 | 被派生类继承,但是不可见 |
protected继承 | 变为protected成员 | 变为protected成员 | 被派生类继承,但是不可见 |
private继承 | 变为private成员 | 变为private成员 | 被派生类继承,但是不可见 |
派生类会继承基类的数据和基类的作用域,所以在派生类可以写同名的成员变量和同名的成员方法
派生类会继承基类构造函数、析构函数以外的所有方法
派生类可以访问从基类继承来的public和protected的成员
派生类对象的构造方式
- 先构造基类成员对象
- 在构造基类自己
- 构造派生类的成员对象
- 构造派生类自己
- 析构则反之
继承中重载、隐藏、重写
- 重载需要在同一个作用域下,函数名相同参数列表不同叫做函数重载
- 重写/覆盖 在基类中有虚函数,当有指针指向对象的时候,编译时期不知道调用哪个类的方法,在运行的时候通过对象的虚函数指针访问虚函数表就可以知道访问哪个类里的方法, 如图二
- 隐藏是在不同的作用域下同名的成员方法/变量,派生类的成员方法/变量 把基类的成员方法隐藏了。如果要调用基类的成员方法需要加上基类的作用域 Base::
基类和派生类对象之间的赋值:
- 基类对象不可以赋值给派生类对象(想当于不能把人(基类)赋值给学生(派生类),可以把学生赋值给人)
- 派生类对象可以赋给基类对象
- 基类的指针(or 引用)指向派生类的对象,可以指向派生类对象但是不可以访问派生类独有的变量和方法,只可以访问基类的成员方法和变量,因为指针的类型是基类,指针解引用只能访问派生类从基类继承而来的方法和变量
- 派生类指针(or 引用)不可以引用基类对象(把基类对象强转之后是可以引用的,用指针访问的时候可能会出现问题)
成为虚函数的两大条件: 能取地址,还得有对象
虚函数表的地址在哪里?? 虚函数表的产生是在编译期就形成了,程序运行,表放在只读数据段里边(生命周期是从程序开始到程序结束,在全局静态区域)
一个类型对应一张表,相同类型定义的对象,不同的虚函数指针指向的同一张虚函数表 如图一
图一
图二
动多态的代码
/****************动多态的代码示范****************/
#if 1
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
//提供了构造函数,编译器就不会提供默认的构造函数了
Base(int a):ma(a){ cout << "Base()" << endl; }
~Base(){ cout << "~Base()" << endl; }
virtual void show(){cout << "Base::show()" << endl;} //基类中加上虚函数
virtual void show(int i){cout << "Base::show(int)" << endl;}
protected:
int ma;
};
class Derive : public Base
{
public:
//在初始化列表没有调用基类的构造函数则编译器会自动调用默认的构造函数,如果编译器没有提供基类的构造函数则会报错
Derive(int data):Base(data), mb(data){ cout << "Derive()" << endl; }
~Derive(){cout << "~Derive()" << endl;}
void show(){cout << "Derive::show()" << endl;} //虚函数
private:
int mb;
};
int main()
{
Derive derive(10);
Base *p = &derive;
p -> show(); //没有虚函数的时候 call Base::show =====> 编译时的绑定
/*
mov eax, dword ptr[p]
mov ecx, dword ptr[eax]
call ecx ===============> 运行时的绑定 运行时的多态
*/
cout<< sizeof(Base) <<endl;
cout<< sizeof(Derive) <<endl;
cout<< typeid(p).name() <<endl;
cout<< typeid(*p).name() <<endl; //访问的是对象RTTI类型信息
return 0;
}
#endif
派生类构造顺序的代码示范
/****************派生类对象的构造方式****************/
#if 1
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
//提供了构造函数,编译器就不会提供默认的构造函数了
Base(int a):ma(a){
cout << "Base()" << endl;
}
~Base(){
cout << "~Base()" << endl;
}
protected:
int ma;
};
class Derive : public Base
{
public:
//在初始化列表没有调用基类的构造函数则编译器会自动调用默认的构造函数,如果编译器没有提供基类的构造函数则会报错
Derive(int data):Base(data), mb(data){
cout << "Derive()" << endl;
}
~Derive(){cout << "~Derive()" << endl;}
private:
int mb;
};
int main()
{
//派生类对象的构造方式
/*先构造基类成员对象,在构造基类自己,构造派生类的成员对象,
构造派生类自己,析构则反之
(根据初始化列表的理解,调用构造函数先执行初始化列表再执行函数体)*/
Derive derive(10);
return 0;
}
#endif
坚持✊