类的种类
1.组合类,一个类是另一个类的组成部分
2.代理类,一个类的接口是另一个类接口的子集
3.继承类,一个类是另一个类的一种
对于类中成员的访问能力进行了测试
//继承:就是将父类中除了private成员,其他的都那一份到子类中进行使用
//继承过程之后子类中父类成员的权限不可能大于父类中的成员
//继承之后,子类中父类成员的权限不可能大于继承权限
class A
{
public:
A(int a)
{
_a = a;
}
int _a;
void funa()
{
funb();
cout << "funa" << endl;
}
protected://子类和自身可以访问
int _b;
void funb()
{
cout << "funb" << endl;
}
private://自身可以访问
int _c;
void func()
{
cout << "func" << endl;
}
};
//构造子类的时候,父类对象一定会在初始化列表中进行构造
//如果父类没有默认构造,就必须自己手动在初始化列表中加上父类的构造
class B :public A//继承权限 不加继承权限默认是私有继承
{
public:
B(int a,int b):A(a)
{
_b = b;
}
int _b;
void fun()
{
funa();
funb();
//func();//父类的私有成员,子类不可以访问
}
};
/*
类中的编译顺序:先编译类名,在编译成员名,在编译成员方法体
组合类的构造/析构顺序:先构造成员对象,在构造自身对象
:先析构自身对象,在析构成员对象
继承类的构造/析构顺序:先构造父类对象,在构造自身对象
:先析构自身对象,在析构父类对象
*/
int main()
{
A _a(1);
_a.funa();
//_a.funb();//protected 成员 外界不可访问
//_a.func();//私有成员
B _b(12,34);
_b.funa();
//_b.funb();子类继承父类的protected权限成员,继承下来后,权限不可能比protected更放开
return 0;
}
当父类和子类的成员函数名字相同时,涉及同名覆盖问题,请看以下代码
class A
{
public:
void funa()
{
cout << "A::funa()" << endl;
}
void funa(int a)
{
cout << "A:: funa(int a)" << endl;
}
};
class B :public A
{
public:
void funb()
{
funa();
cout << "B::funb()" << endl;
}
void funa()
{
cout << "B::funa()" << endl;
}
};
/*
子类中的成员,会隐藏父类中同名的成员---同名隐藏
如果想要访问被子类隐藏的父类成员,只需要加上父类作用域即可
*/
int main()
{
A a;
a.funa();
B b;
b.funb();
b.A::funa();//访问被子类同名隐藏的父类成员
return 0;
}
多态
静多态—编译时期的多态
函数重载,模板
动多态—运行时期的多态
继承中的多态
虚函数
使用virtual关键字修饰的普通成员方法
子类继承父类时候,如果子类中的成员方法对应父类中相同(同函数名,同参数列表的)的方法是虚函数
则子类中对应的成员方法也是虚函数
vfptr(虚表指针)----vftable(虚表)存储虚函数的指针
动多态的实现要素
1.调用的必须是虚函数
2.必须是指针或者引用调用
3.对象必须完整–构造函数调用完成 && 析构函数没有开始调用
动多态产生原理:
1.类中的虚函数指针会存在vftable中
2.继承过程中,子类中相同的成员方法会覆盖vftable中已有的虚函数指针
子类中的成员方法会在vftable中覆盖父类相同的虚方法
动多态调用过程:
当时用指针调用虚方法时候
1.判断为虚方法
2.通过vfptr找到vftable
3.在vftable中找到对应的成员方法指针
4.调用那个找到的成员方法指针
class A
{
public:
int* _a;
A()
{
this->fun();//没有产生动多态 对象必须完整
cout << "A()" << endl;
_a = new int();
}
virtual void fun()//虚函数
{
cout << "A::fun()" << endl;
}
void fun(int a)
{
cout << "A::fun(int a)" << endl;
}
virtual ~A()
{
cout << "~A()" << endl;
delete _a;
}
};
//父子类的成员方法之间存在两种关系:同名隐藏,相同虚函数覆盖
class B :public A
{
public:
B()
{
cout << "B(int a,int b)" << endl;
_b = new int();
}
int* _b;
void fun()
{
cout << "B::fun()" << endl;
}
void fun(int b)
{
cout << "B::fun(int b)" << endl;
}
~B()
{
cout << "~B()" << endl;
}
};
int main()
{
A a;
a.fun();
B b;
b.fun();
A* p = new A();
p->fun();//A::fun()
//父类指针指向子类对象
A* p1 = new B();
p1->fun();//当未写虚函数时,调用A::fun(),当写入虚函数时调用B::fun()
//简单根据类型去调用
delete p1;//当A的析构函数不加virtual时,打印结果为 A() B(int a,int b) B::fun() ~A
//当A的析构函数加上virtual时,打印结果为 A() B(int a,int b) B::fun() ~B()~A()
//先析构子类对象,在析构父类对象
return 0;
}
面试题:什么情况下析构函数必须写成虚函数?
存在父类指针指向堆上的子类对象时候,必须将父类的析构函数写成虚函数