C++ 指针修改对象的成员变量and 多态(mooc)

  • 内存中类的对象里没有方法,只有成员变量(和结构体只有变量一样)。
    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

原文地址:https://www.cnblogs.com/bneglect/p/14660863.html