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"