目录:
- 继承的本质和原理
- 派生类的构造过程
- 重载覆盖 隐藏
- 静态绑定和动态绑定
- 多态 vfptr和vftable
- 抽象类的设计原理
- 多重继承以及问题
- 虚基类 vbptr和vbtable
- RTTI
- c++四种类强转
- 继承多态常见笔试面试题目分享
1、继承的本质和原理:
继承方式: 基类的访问限定 派生类的访问限定 main外部的访问限定
三种:私有全私有
保护二级私有
公有一级私有
class A
{
public:
int ma;
protected:
int mb;
private:
int mc;
};
class B :public A
{
public:
void func()
{
cout << "func: " << func << endl;
}
int md;
protected:
int me;
private:
int mf;
};
class C :public B
{
//在c里面,请问ma的访问限定的是什么?
//私有的
};
2、派生的构造过程
* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
#if 0
/*
* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
*/
class Base
{
public:
Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
~Base() { cout << "~Base()" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data = 20) :mb(data), Base(data)
{
cout << "Derive()" << endl;
}
~Derive()
{
cout << "~Derive()" << endl;
}
private:
int mb;
};
int main()
{
Derive v;
/*
输出:
Base()
Derive()
~Derive()
~Base()
先调用派生类的析构函数
*/
return 0;
}
#endif
3、重载 隐藏 覆盖
图像分析:
从图中可以看出来有危险。
/*重载 隐藏 覆盖
1、重载关系
必须处在同一个作用域中,函数名字相同,参数列表不同
2、隐藏:作用域的隐藏
*/
class Base
{
public:
Base(int data = 10) :ma(data) {}
void show() { cout << "Base::show()" << endl; }
void show(int) { cout << "Base::show(int)" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data = 20) :Base(data), mb(data) {}
void show() { cout << "Derive::show" << endl; }
private:
int mb;
};
int main()
{
#if 0
Derive d;
d.show();
d.Base::show(); //这样子才能调用基类的
//d.show(10); 优先查看派生类自己的作用域成员,没有的话才去基类里面找
d.Base::show(10);
#endif
Base b(10);
Derive c(20);
//基类到派生类的转换
b = c; //类型从下到上的转化
//d = b;//类型从上到下的转换 error
//基类指针 引用 <-派生类对象 默认派生类到基类的转换
Base* pb = &c; //只能访问派生基类部分的成员
pb->show();
pb->show(10);
/*Base::show()
Base::show(int)
*/
((Derive*)pb)->show(); //这样子就调用了派生类的show
//在继承结构中进行上下的类型的转换,默认支持从下到上的转换
Derive* pd = (Derive*)&b; //派生类的show方法 没有派生类对象
//b是基类哇 不安全涉及内存的非法访问
pd->show();
return 0;
}
4、虚函数、静态绑定、动态绑定
/*
* 虚函数 ,静态绑定 和动态绑定
* 总结一:如果类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生了一个唯一的vftable虚函数表,
*虚函数表中的主要存储的内容就是RTTI指针和虚函数的地址 ,程序运行中,每一张虚函数表都会记载到内存的.rodata区
*
* 总结二:
* 一个类里面定义了函数,那么这个类定义的对象运行的时候,内存中开始 部分,多存储了vfptr虚函数指针,指向相应类型 的虚函数表vftable
* 一个类型定义的n个对象,他们的vfptr指向都是同一张虚函数表
*
* 总结三:
* 一个类里卖弄虚函数的个数,不影响对象内存大小(vfptr),影响的是虚函数表的大小
*
* 总结四:
* 如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
* 而且基类的方法都是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
*/
class Base
{
public:
Base(int data = 10) :ma(data) {}
//虚函数
virtual void show() { cout << "Base::show()" << endl; }
//虚函数
virtual void show(int) { cout << "Base::show(int)" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data = 20) :Base(data), mb(data) {}
void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数
private:
int mb;
};
int main()
{
Derive d(50);
Base* pb = &d;
pb->show(); //静态(编译时期)的绑定 (函数的调用)
//如果发现是虚函数,就进行动态绑定了
pb->show(10);
/*
E8 0B EE FF FF call Base::show (07FF6900A13F7h)
BA 0A 00 00 00 mov edx,0Ah
48 8B 4D 28 mov rcx,qword ptr [pb]
E8 E2 EA FF FF call Base::show (07FF6900A10DCh)
*/
cout << sizeof(Base) << endl;
cout << sizeof(Derive) << endl;
cout << typeid(pb).name() << endl;
/*如果Base没有虚函数,*pb识别的就是编译时期的 如果有虚寒申诉就是运行时期的*/
cout << typeid(*pb).name() << endl;
/*
Derive::show
Base::show(int)
16
24
class Base * __ptr64
class Derive
| vptr (8 字节) | ma (4 字节) | 填充 (4 字节) |
*/
return 0;
}
/*
* Base::show()
Base::show(int)
4
8
class Base * __ptr64
class Base
*/
5、多态
/*
解释多态:
静态编译时期:函数重载
编译阶段就已经确定好的;
动态编译时期:
在继承中,基类指针引用指向派生类的对象,通过该指针引用调用同名的覆盖方法(虚函数)基类指针指向哪个派生类对象,就会调用哪个派生类对象同名覆盖的方法,称为多态。
访问谁的vfptr=>继续访问谁的vftable;调用对应的派生类对象的方法
*/
class Animal
{
public:
Animal(string name) :_name(name) {}
virtual void bark()
{
}
protected:
string _name;
};
class Cat :public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << "猫叫" << endl; }
};
class Dog :public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << "狗叫" << endl; }
};
void bark(Animal* p)
{
p->bark();
}
int main()
{
Cat a("wocao");
Dog b("shabi");
bark(&a);
bark(&b);
return 0;
}
6、理解抽象类
//汽车的基类
class Car
{
public:
Car(string name, double oil) :name(name), oil(oil) {}
double getLiftMiles()
{
return oil * getMilesPersonGallon();
}
public:
string name;
double oil;
virtual double getMilesPersonGallon() = 0; //纯虚函数
};
class Bnze :public Car
{
public:
Bnze(string name, double oil) :Car(name, oil) {}
double getMilesPersonGallon() { return 20.0; }
};
class Audi :public Car
{
public:
Audi(string name, double oil) :Car(name, oil) {}
double getMilesPersonGallon() { return 18.0; }
};
void showCarLeftMiles(Car& car)
{
cout << "Car name:" << car.name << endl << car.getLiftMiles() << endl;
}
int main()
{
Audi car("1", 12);
Bnze car1("2", 12);
showCarLeftMiles(car);
showCarLeftMiles(car1);
return 0;
}
7、再谈动态绑定
/*虚函数和动态绑定 问题:是不是虚函数的调用一定就是动态绑定? 不是
类的构造函数中,调用虚函数不会发生动态绑定
*/
class Base
{
public:
Base(int data = 10) :ma(data) {}
//虚函数
virtual void show() { cout << "Base::show()" << endl; }
//虚函数
virtual void show(int) { cout << "Base::show(int)" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data = 20) :Base(data), mb(data) {}
void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数
private:
int mb;
};
int main()
{
Base b;
Derive d;
/*
E8 6F E9 FF FF call Base::Base (0121118h)
6A 14 push 14h
8D 4D DC lea ecx,[d]
E8 24 E9 FF FF call Derive::Derive (01210D7h)
对象本身调用虚函数是静态绑定
*/
b.show();
d.show();
//动态绑定
Base* pb1 = &b;
pb1->show();
Base* pb2 = &d;
pb2->show();
//动态绑定
Base& rb1 = b;
rb1.show();
Base& rb2 = d;
rb2.show();
//动态绑定
Derive* p = (Derive*)&b;
p->show();
//如果不是引用或者指针调用则是静态调用
return 0;
}
8、虚析构函数
/*
/*
1、哪些函数不能实现虚函数
虚函数依赖:
1虚函数能产生地址,存储在虚函数表vftable中
2、对象必须存在(vfptr->vftable->虚函数地址)
构造函数:
1、virtual + 构造函数
2、构造函数中华调用的任何函数都是静态绑定的调用虚函数,也不会产生静态绑定派生类的对象构造过程。
1、先调用的是基类的构造函数2、才调用派生类的构造函数
static静态成员方法 No virtual + static
问题二:
虚析构函数 析构函数调用的时候存在,对象是存在的!
什么时候吧基类的析构函数必须实现成虚函数?
基类的指针(引用)指向堆上new出来的派生类对象的时候,delete pb(基类的指针)
,它调用析构函数的时候必须发生动态绑定,否则会导致派生类的析构函数无法调用
*/
class Base
{
public:
Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
virtual ~Base() { cout << "~Base()" << endl; }
//虚函数
virtual void show() { cout << "Base::show()" << endl; }
//虚函数
virtual void show(int) { cout << "Base::show(int)" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data = 20) :Base(data), mb(data) { cout << "Derive()" << endl; }
//基类的析构函数是虚函数,派生类的析构函数默认成为虚函数
~Derive() { cout << "~Derive()" << endl; }
void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数
private:
int mb;
};
int main()
{
Base* pb = new Derive(10);
pb->show();//动态绑定
delete pb;//派生类的析构函数,没有呗调用
/*
pb->Base Base::~Base 对于析构函数的调用就是静态绑定了
发现是虚析构函数就是动态绑定了
call Base::~Base
*/
return 0;
}
9、题目分享
class Animal
{
public:
Animal(string name) :_name(name) {}
virtual void bark() = 0;
protected:
string _name;
};
class Cat :public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << "猫叫" << endl; }
};
class Dog :public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << "狗叫" << endl; }
};
int main()
{
Animal* p1 = new Cat("加菲猫");
Animal* p2 = new Dog("二哈");
int* p11 = (int*)p1;
int* p22 = (int*)p2;
int tmp = p11[0]; //cat的前四个字节 vfptr -> Dog vftable
p11[0] = p22[0]; //dog的前四个字节 vfprt ->Cat vftable
p22[0] = tmp;
p1->bark(); //cat vfptr -> dog vfptr->dog vftable
p2->bark();
delete p1;
delete p2;
return 0;
}
class Base
{
public:
virtual void show(int i = 10)
{
cout << "call Base::show i" << i << endl;
}
};
class Derive :public Base
{
public:
void show(int i = 20)
{
cout << "call Derive ::show i " << i << endl;
}
};
int main()
{
Base* p = new Derive();//虚析构函数
/*
* push 0Ah =》函数调用参数压栈的在编译时期就确定好的 是基类的10
* mov eax,dword ptr[p];
* mov ecx,dword ptr[eax]
* call ecx;
*/
p->show();
delete p; //动态绑定 p->Derive vfptr ->Derive vftable
return 0;
}
class Base
{
public:
virtual void show()
{
cout << "call Base::show " << endl;
}
};
class Derive :public Base
{
private: //改成私有还能调用吗?
void show()
{
cout << "call Derive ::show " << endl;
}
};
int main()
{
Base* p = new Derive();
/*
* 成员方法能不能调用就是说方法的访问权限是不是public,实在编译时期就需要确定的
* 只能看到Base::show
* 基类:call Base::show静态
* call Derive::show动态
*/
p->show(); //最终调用是在运行时期才确定的 这个调用的call Derive show()
delete p;
return 0;
}
class Base
{
public:
Base()
{
/*
push ebp
mov ebp,esp
sub esp,4Ch
rep stos esp<->ebp
vfptr <= &Base::vftable
*/
cout << "call Base()" << endl;
clear();
}
void clear() { memset(this, 0, sizeof(*this)); }
virtual void show()
{
cout << "call Base::show()" << endl;
}
};
class Derive :public Base
{
public:
/*
push ebp
mov ebp,esp
sub esp,4Ch
rep stos esp<->ebp
vfptr <= &Derivev::vftable
*/
Derive()
{
cout << "call Derive ()" << endl;
}
void show()
{
cout << "call Derive::show()" << endl;
}
};
int main()
{
//Base* p = new Base();
//p->show();//动态绑定
//delete p;
Base* p1 = new Derive();
p1->show(); //动态绑定
delete p1;
return 0;
}