书上讲
在面向对象语言中,对于接口的不同实现方式称之为多态。多态性时允许将父类对象设置成为一个或者多个他的子类对象相等的技术一般用指针实现,赋值之后,父类对象可以根据当前赋值给他的子类对象的特性以不同方式进行调用。
那么上面说的是怎么做到的呢?
我们看下面这个代码
#include <stdio.h>
#include <stdint.h>
static int body = 0;
class Base
{
public:
Base() {
printf("construct base\n");
}
virtual ~Base() {
printf("release base\n");
}
virtual void func() {
printf("run func\n");
}
void Dump() {
printf("dump me\n");
}
};
class BaseA: public Base
{
public:
BaseA() {
printf("construct baseA\n");
}
virtual ~BaseA() {
printf("release baseA\n");
}
void func() {
printf("run BaseA func\n");
}
};
void dump(const char *info, char *addr)
{
int64_t *d = (int64_t*)addr;
printf("%s content %ld\n", info, *d);
}
int main()
{
Base *b = new BaseA();
b->func();
b->Dump();
char *addr = (char *)b;
dump("class son", addr);
delete b;
Base *c = new Base();
char *addr1 = (char*)c;
dump("class base", addr1);
delete c;
Base *e = new BaseA();
e->func();
char *addr3 = (char *)e;
dump("class son", addr3);
delete e;
Base *d = new Base();
char *addr2 = (char*)d;
dump("class base", addr2);
delete d;
printf("body %p\n", &body);
return 0;
}
我们知道虚函数式通过虚函数表调用的,对象里面最前面都存放了虚函数表的首地址,就像我上面打印出来的。这个表应该是个只读数据段。
基类 *p = new 派生类
C++允许派生类指针直接赋值给基类,虽然对象类型变了,但是new的内容没有变,还是派生类。
那么它的虚函数表指针肯定是派生类的。现在有派生类虚函数表了,那是怎么调用到派生类的虚函数方法的呢?
看下反汇编,
通过上面的图很容易看到,其实虚函数的调用和普通函数是不一样的,
虚函数是通过虚函数表的地址,偏移,然后取到的一个函数指针,然后执行这个函数指针指向的函数。而普通的函数,直接有自己的地址,直接callq就可以了。
2 接下来我们来探讨另一个问题, 这种情况的装饰模式怎么实现的呢?
常见的装饰模式代码,
class Base
{
virtual int Func();
};
class BaseA:public Base
{
int Func() {
Base::Func();
printf("run Func\n");
}
};
我们反汇编一下,看下是如何调用的。
看起来域操作符可以直接解除virtual的限制。
直接调用到了Base类的被重写的方法。