- 内存中类的对象里没有方法,只有成员变量(和结构体只有变量一样)。
class A { public : int i; A():i(10){} } void main() { A a; int *p = (int *) &a; // 把对象a的地址赋值给指针p *p = 50; //ok,把i的值改成50了 }
之所以可以通过指针修改对象里面的成员变量(即便是private 也能修改),是因为对象里只有成员变量,没有函数。则对象的地址就是第一个成员变量的地址(和结构体是一样的)
- upcast(向上造型):子类对象会被当做父类对象看待。有一个父类A, 有一个子类B。把B的对象b 地址赋值给指针 bp ,假设A类中有一个print 函数。那么通过对象b 的指针访问ep->print(); 它实际调用的却是A类的print,而不是B类的print。之前讲过一个A和B都有一个相同的函数,那么A类的函数会被隐藏。而通过指针这个则不会隐藏A的函数,而是会调用A的函数。不调用B的。
- 多态的实现需要两个特性:upcast 和 动态绑定。动态绑定需要virtual。动态绑定:可以理解为PHP里的后期静态绑定 static::
class A { public : int i; A():i(0){} ~A(){} virtual void render(){//todo} // virtual 就是让render函数和子类的相同函数产生了联系。 }
class B
{
public:
virtual void render(){} //这里加不加virtual,都是默认有的。只要一个类的函数有virtual,那么他的子孙类中相同函数默认就都有。不过最好加上virtual,为了方便别人阅读和理解
} void render(A* p) { p->render(); // 指针配合virtual 实现了 动态绑定。 }
void main()
{
B b;
render(&b); //如果没有virtual, 那么根据upcast,会调用父类A 的render函数,而现在render 有virtual 修饰,所以调用的是B类的render函数。
//也就是实际传的是哪个对象的指针,就调用哪个一个类的函数
}静态绑定:就是不带virtual,也就是正常的调用,指定哪个调用哪个。
- 有 virtual (虚函数) 的类比正常的类大一点(内存)。所有有virtual的类的对象的头(存储对象的内存块头部)有一块内存用来存储vptr指针(这个指针是隐藏的,C++不让外面知道它的存在)。这个vptr指针指向vtable。
class A { public: int i; A():i(10){} virtual void print() {cout << i << endl;} } void main() { cout << sizeof(a) << endl; A a; int *pa = (int *) &a; cout << *pa << endl; // 输出的不是i 的值,而是vptr的值 pa++; cout << *pa << endl; //这时候输出的才是i 的值。得以验证对象的头不存储的是vptr 的指针。 }
vtable里存储的是virtual 函数的地址。vtable 是属于类的,不属于对象。所以同一个类的vtable肯定是一样的。动态绑定就是通过vtable实现的。在vtable里把virtual 函数的地址改一下就实现了动态绑定。见下图(mooc视频讲解vtable)