子类重载虚函数
在C++中,子类可以重载基类的虚函数,这是C++语法中一个重要的多态。
例如,ClassB
继承自ClassA
,且重载了基类的一个函数,则被调用时将会触发重载的版本:
#include<iostream>
using namespace std;
class ClassA
{
public:
virtual void TestFunc()
{
cout << "ClassA::TestFunc" << endl;
}
};
class ClassB : public ClassA
{
public:
virtual void TestFunc() override
{
cout << "ClassB::TestFunc" << endl;
}
};
int main()
{
ClassB* pB = new ClassB();
pB->TestFunc();
}
输出:
ClassB::TestFunc
这和指针的类型没有关系,就算将指针类型改为ClassA
,只要对象仍旧是ClassB
,那么结果一样:
int main()
{
ClassA* pA = new ClassB();
pA->TestFunc();
}
ClassB::TestFunc
问题
但这里有一个问题是:
一个ClassB
类型的对象,只要重载了TestFunc
,那么ClassA
版本的TestFunc
那在任何时候都不会被调用吗?
实验后发现并不是这样——在ClassB
的构造函数中会调用ClassA
版本的构造函数,而如果在ClassA
的构造函数中调用了TestFunc
,那么被调用的是ClassA
的版本。
下面是实验:
实验在基类的构造函数中调用被子类重载的虚函数
在ClassA
的构造函数中调用会被重载的函数TestFunc
ClassA()
{
TestFunc();
}
在main
中创建ClassB
类型的对象
int main()
{
ClassB* pB = new ClassB();
}
那么结果是:
ClassA::TestFunc
也就是说:
在基类的构造函数中调用会被子类重载的虚函数,调用的版本是基类的版本。
关于为何是这样,我还没有较深的理解。我不清楚C++标准中是否针对这种用法有明确的规定;也不确定编译器在处理这一问题上是否有差别。
不过,这一问题要留意,因为在实际编程时确实会造成困惑。
在基类的构造函数中调用纯虚函数会怎么样?
既然普通的虚函数不行,那么“纯虚函数”会怎么样呢?毕竟基类没有它的实现
实验1
将TestFunc
改为纯虚函数:
class ClassA
{
public:
ClassA()
{
TestFunc();
}
virtual void TestFunc() = 0;
};
这样会出现链接错误:
1>testV.obj : error LNK2019: 无法解析的外部符号 "public: virtual void __cdecl ClassA::TestFunc(void)" (?TestFunc@ClassA@@UEAAXXZ),该符号在函数 "public: __cdecl ClassA::ClassA(void)" (??0ClassA@@QEAA@XZ) 中被引用
1>D:\0_WorkSpace\Cpp\TestMiscs\x64\Debug\TestMiscs.exe : fatal error LNK1120: 1 个无法解析的外部命令
实验2
那么再“耍一些诡计”:
让构造函数调用另一个函数,而后者去调用纯虚函数:
class ClassA
{
public:
ClassA()
{
Test2();
}
virtual void TestFunc() = 0;
void Test2()
{
TestFunc();
}
};
编译成功了,然而运行时会报错:
看来,无论怎么样,在基类的构造函数中都无法调用被子类重载的函数。(至少在我目前的环境下是这样)