C++中关键字virtual主要用于多态,子类重写父类方法,从而提供更灵活的实现逻辑. 在QT里面,我们经常会继承QWidget, 然后在子类中重写mouseMoveEvent()这些事件,当鼠标移动时系统执行子类方法逻辑,一直都很纳闷这是怎么实现的, 如何将子类函数指针传入到父类, 当相应事件发生时,进而执行子类鼠标移动方法.
1. virtual的基础用法
// 父类 class A { public: A(){} ~A(){} virtual void print() { printf("A hello\n"); } }; // 子类 class B:public A { public: B():A(){} ~B(){} public: void print() { printf("B hello\n"); } }; // 测试 B *b=new B; A *a=b; // 重点:将A指针指向B a->print(); // 此时执行B类方法,打印 "B hello"
通过上面代码发现,直接将A指向B,就能执行B方法. 这样我们可以在构造方法时,将类指针指向当前类,就达到了注入子类的目的
2. 传入子类指针
// 前置声明,增加中间类,用来初始化子类指针 class C; // 父类 class A { friend class C; public: A(); { c=new C; // 初始化成员指针 init(); } ~A(){} void init() { c->a=this; // a指向当前类,如果当前类为派生类,则this指针就表示派生类,这样就传入了子类指针 /// 错误 c->run(); // 调用方法 } virtual void print() { printf("A hello\n"); } private: C *c; }; // 子类 class B:public A { public: B():A(){} ~B(){} void print() { printf("B hello\n"); } }; // 增加C类,用来对成员变量A赋值 class C:public QThread { public: C(){} ~C(){} void run() { a->print(); } private: A *a; }; // 测试 B *b=new B(); // 打印 "A hello"
当调用B构造函数时, 首先构造A,执行c->a=this; 将B赋值给A, 接下来执行run()方法, 按理说应该打印B的print方法,怎么仍然执行A了,难道在构造B时,执行A构造方法时,此时的this不是B. 经打印发现 this 的地址值0x3d2f10与B一致, 原来此时B未完成实例化,系统无法执行B方法.
3. 从构造函数中分离任务,让其完成实例化,从而执行B类方法
// 前置声明,增加中间类,用来初始化子类指针 class C; // 父类 class A { friend class C; public: A() { c=new C; init(); // 初始化 } ~A(){} void init() { c->a=this; // a指向当前类,如果当前类为派生类,则this指针就表示派生类,这样就传入了子类指针 } virtual void print() { printf("A hello\n"); } private: C *c; }; // 子类 class B:public A { public: B():A(){} ~B(){} void print() { printf("B hello\n"); } }; // 增加C类,用来对成员变量A赋值 class C:public QThread { public: C() { start(); // 启动线程 } ~C(){} void run() { sleep(2); a->print(); } private: A *a; }; // 测试 B *b=new B(); // 成功打印 "B hello"