(一)类的编译顺序,构造顺序,析构顺序
类的编译顺序:类名->成员名->成员方法体
类的构造顺序:成员对象->类对象
子类构造顺序:父类->子类
子类析构顺序:子类->父类
(二)重载,隐藏,覆盖
重载:函数名相同 参数列表不同 作用域要相同
隐藏:子类隐藏父类中同名的成员方法
覆盖:子类覆盖父类中相同的虚成员方法 (相同表示 同名 同返回值 同参数列表)
(三)四种类型强转
(1)const_cast:去掉const属性的类型强转
如:const int a = 1; int b = const_cast
(2)static_cast:编译器认为安全的
如:int a; double b = static_cast
(3)dynamic_cast:运行时期的类型强转,依靠RTTI(重要)
1.必须是指针
2.必须是继承关系的类型指针
3.有RTTI(存在于虚表里,运行时的类型),即有虚表
(4)reinpreter_cast:强制转换 (int)a
(四)虚表什么时候产生?存放在哪里?
答:存放在.rodata段,数据段编译期生成
(五)静多态,动多态 静多态:编译时期决定的多态 如:重载(编译期调用),模板
动多态:运行时期的多态,运行时才知道调用哪个成员方法。如:继承中的多态
(六)动多态调用的条件:
1.利用指针或引用调用
2.调用该类型下的虚函数
3.对象完整(完整表示构造函数之后~析构函数之前)
(七)基类指针指向指针对象,指针是什么类型? *指针是什么类型(基类中有虚函数)
父类:Base 子类:Drive
指针:class Base*类型
*指针:class Drive类型
class Base
{
public:
virtual void show()
{
cout<<"Base::show()"<
(八)基类存在虚函数,子类覆盖,子类的内存布局
Base::
vfptr
val
Derive::val
//子类继承父类的所有成员变量和方法,加上子类特有的成员变量和方法
(九)基类无虚函数,子类有虚函数,子类的内存布局,基类指针指向堆上的子类对象,delete指针,崩溃,重载子类new基类delete打印指针值,指针指向子类对象基类开始部分
class Base
{
public:
void show()
{
cout << "Base::void show()" << endl;
}
void operator delete(void *p)
{
cout << p << endl;
free(p);
}
};
class Derive : public Base
{
public:
virtual void show()
{
cout << "Derive :: virtual void show()" << endl;
}
void *operator new(size_t size)
{
void *p = malloc(size);
cout << p << endl;
return p;
}
};
/*
vfptr
Base::-------------
Derive::
*/
int main()
{
Base * pb = new Derive();
pb->show();
delete (pb-4);
return 0;
}
(十)基类定义虚函数和普通重载函数,子类定义同名函数隐藏普通函数和函数
#include
using namespace std;
class Base
{
public:
//以下两种方法构成重载
void show()
{
cout<<"Base::void show()"<show(); //Base:void show()
base->show(10); //Base:virtual void show(10)
derive->show(); //Derive:void show()
derive->Base::show(); //Base:void show()
return 0;
}
(十一)基类指针指向堆上的子类对象,此时delete指针,子类析构函数未调用,有可能造成内存泄漏,基类析构函数写成虚函数
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
virtual ~Base()
{
cout << "~Base()" << endl;
}
};
class Derive : public Base
{
public:
Derive()
{
cout << "Derive()" << endl;
}
~Derive()
{
cout << "~Derive()" << endl;
}
};
int main()
{
Base* base = new Derive();
delete base;
return 0;
}
//Base()
//Derive()
//~Derive()
//~Base()
(十二)基类构造清除所有数据,基类指针调用自己的虚函数,子类调用虚函数
class Base
{
public:
Base()
{
memset(this, 0, sizeof(Base)); //vfptr 00000000
}
virtual void show()
{
cout << "Base::virtual void show()" << endl;
}
};
class Derive : public Base
{
public:
Derive()
:Base()
{
}
void show()
{
cout << "Derive::void show()" << endl;
}
};
int main()
{
//Base *pb = new Base();
//pb->show();error
//执行出错。当基类构造的时候将空间内所有字节都置为0,包括vfprt
//当访问虚函数的时候就会进入地址为0的空间,访问出错。
Derive* pd = new Derive();
pd->show();
//正常执行,虽然基类构造的时候将空间内所有字节包括vfptr都置为0
// 但是子类在构造的时候将vfprt又置为自己的Show函数入口地址,因此访问正常
return 0;
}
(十三)基类和派生类构造函数、析构函数中调用虚函数,会不会产生多态调用
答:不会,因为此时对象不完整。对象在构造函数结束到析构函数之前是完整的。
(十四)基类虚函数放到共有,派生类虚函数放到私有,外部基类指针指向子类对象,能否派生类私有函数否访问?
答:可以,因为该指针是基类的指针,但是其又可以指向子类的示例,因此在调用之前(进入虚函数表中函数地址之前)无法确定调用的究竟是谁。
class Base
{
public:
virtual void show(int)
{
cout << "Base :: virtual void show(int)" << endl;
}
};
class Derive : public Base
{
public:
private:
void show(int)
{
cout << "Derive :: void show(int)" << endl;
}
};
int main()
{
Base* pb = new Derive();
pb->show(10);
return 0;
}
(十五)基类和子类给不同的函数参数默认值,基类指针指向子类对象,调用函数
class Base
{
public:
virtual void show(int a = 20)
{
cout << "a=" << a <<"==================" <show(); //a = 20
return 0;
}
(十六)设计一个函数打印学生,老师信息 抽象类,继承