虚函数
在之前,我曾经在多态中简单的提到过虚函数,虚函数就是用virtual关键字去修饰类的成员函数,然后将这个函数在派生类中去重新实现。
以代码为例:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class B
{
public:
virtual void Test1(int i)
{
cout << "B:Test1()" << endl;
}
virtual void Test2(int i)
{
cout << "B:Test2()" << endl;
}
void Test3(int i)
{
cout << "B:Test3()" << endl;
}
};
class C: public B
{
public:
virtual void Test1(int i)
{
cout << "C:Test1()" << endl;
}
void Test2(int i)
{
cout << "C:Test2()" << endl;
}
virtual void Test3(int i)
{
cout << "C:Test3()" << endl;
}
};
int main()
{
B* b = new C;
b->Test1(0);
b->Test2(0);
b->Test3(0);
return 0;
}
其中Test1就是进行了重写的虚函数,而Test2没有进行重写,Test3则是不是虚函数,所以这里的Test2和Test3都没有实现动态多态,只有Test1实现了动态多态。
之前强调过的我在这里再强调一遍,动态多态有两个条件:
①必须是虚函数;②必须通过基类类型的指针或引用进行调用。
纯虚函数
介绍了虚函数,那么就得提到纯虚函数。纯虚函数就是在成员函数的形参后面写上=0,那么成员函数就是纯虚函数。而包含纯虚函数的类就叫做抽象类(又叫接口类)。但是需要注意的是, 抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。上面说了虚函数,大体的解释了一下虚函数的使用方式,但是,在类中使用虚函数的时候,有一些注意事项你还是需要注意的:
①派生类重写基类中的虚函数实现多态,函数名,参数列表,返回值全都要一样。(协变除外,协变是返回值不一样);
②派生类中重写的虚函数要始终保持与基类中虚函数的特性一致;
③只有类中的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数;
④构造函数不能定义为虚函数,但是最好将基类的析构函数声明为虚函数,另外operate=定义为虚函数也是可以的,但是最好不要这样做,容易导致混淆;
⑤不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为;
⑥如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加;
⑦虚表是所有类对象实例共用的。
虚表
既然在上面的注意事项中写到了虚表,那么在这里就得说一下虚表并进行虚表剖析。
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。
以代码为例:
class Test
{
public:
Test()
{
i = 10;
cout << "this = " << this << endl;
}
virtual ~Test()
{}
private:
int i;
};
int main()
{
Test t;
cout << sizeof(t) << endl;
return 0;
}
通过这个图片我们可以发现,在创建带有虚函数的类时,编译器会帮助维护一张虚表,其中 对象的前四个字节就是指向虚表的指针。
下面我们将分析没有覆盖和有覆盖两种情况。
没有覆盖:
代码:
class Base
{
public:
Base(){ i = 10; }
virtual void FunTest0(){ cout << "Base::FunTest0()"; }
virtual void FunTest1(){ cout << "Base::FunTest1()"; }
virtual void FunTest2(){ cout << "Base::FunTest2()"; }
private:
int i;
};
class Derived :public Base
{
public:
virtual void FunTest4(){ cout << "Derived::FunTest4()"; }
virtual void FunTest5(){ cout << "Derived::FunTest5()"; }
virtual void FunTest6(){ cout << "Derived::FunTest6()"; }
};
typedef void(*FUN_TEST)();
void FunTest()
{
Base b;
cout << "Base vfptr:" << endl;
for (int i = 0; i < 3; ++i)
{
FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&b + i));
funTest();
cout << ": " << (int *)funTest << endl;
}
cout << endl;
Derived d;
cout << "Derived vfptr:" << endl;
for (int i = 0; i < 6; ++i)
{
FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&d + i));
funTest();
cout << ": " << (int *)funTest << endl;
}
}
int main()
{
FunTest();
return 0;
}
由代码进行分析:
有覆盖:
代码:
class Base
{
public:
virtual void FunTest0(){ cout << "Base::FunTest0()" << endl; }
virtual void FunTest1(){ cout << "Base::FunTest1()" << endl; }
virtual void FunTest2(){ cout << "Base::FunTest2()" << endl; }
virtual void FunTest3(){ cout << "Base::FunTest3()" << endl; }
};
class Derived :public Base
{
public:
virtual void FunTest0(){ cout << "Derived::FunTest0()" << endl; }
virtual void FunTest1(){ cout << "Derived::FunTest1()" << endl; }
virtual void FunTest4(){ cout << "Derived::FunTest4()" << endl; }
virtual void FunTest5(){ cout << "Derived::FunTest5()" << endl; }
};
typedef void(*_pFunTest)();
void FunTest()
{
Base base;
for (int i = 0; i < 4; ++i)
{
_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&base + i));
pFunTest();
}
cout << endl;
Derived derived;
for (int i = 0; i < 6; ++i)
{
_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&derived + i));
pFunTest();
}
}
void TestVirtual()
{
Base base0;
Derived derived;
Base& base1 = derived;
}
int main()
{
FunTest();
TestVirtual();
return 0;
}
由代码进行分析:
下面我再简单剖析一下多重继承中有无虚函数覆盖的情况:
多重继承:没有虚函数覆盖
代码:
class Base0
{
public:
Base0()
{
i = 0xB0;
}
virtual void PrintB0()
{
cout << "i = " << hex << i << " Base0::PrintB0()" << endl;
}
int i;
};
class Base1
{
public:
Base1()
{
i = 0xB1;
}
virtual void PrintB1()
{
cout << "i = " << hex << i << " Base1::PrintB1()" << endl;
}
int i;
};
class Base2
{
public:
Base2()
{
i = 0xB2;
}
virtual void PrintB2()
{
cout << "i = " << hex << i << " Base2::PrintB2()" << endl;
}
int i;
};
class Derived :public Base0, public Base1, public Base2
{
public:
Derived()
{
i = 0xD0;
}
virtual void PrintD()
{
cout << "i = " << hex << i << " Derived::PrintD()" << endl;
}
int i;
};
typedef void(*VFTABLE_FUN)();
void PrintVfPTab(char * _pStr, int *_pVfAddr)
{
cout << _pStr << endl;
for (int i = 0;; i++)
{
VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + i));
if (NULL == pPrintVTab)
{
break;
}
pPrintVTab();
cout << (int*)pPrintVTab << endl;
}
cout << endl;
}
void FunTest()
{
Derived derived;
cout << sizeof(derived) << endl;
int *pVfTAddr = NULL;
Base0& base0 = derived;
pVfTAddr = (int*)(*(int *)&base0);
PrintVfPTab("Base0 virtual Tab:", pVfTAddr);
Base1& base1 = derived;
pVfTAddr = (int*)(*(int *)&base1);
PrintVfPTab("Base1 virtual Tab:", pVfTAddr);
Base2& base2 = derived;
pVfTAddr = (int*)(*(int *)&base2);
PrintVfPTab("Base2 virtual Tab:", pVfTAddr);
pVfTAddr = (int*)(*(int *)&derived);
PrintVfPTab("Derived virtual Tab:", pVfTAddr);
derived.PrintB0();
derived.PrintB1();
derived.PrintB2();
derived.PrintD();
}
int main()
{
FunTest();
return 0;
}
由代码进行分析:
多重继承:有虚函数覆盖
代码:
class Base0
{
public:
Base0(){ i = 0xA0; }
virtual void Print(){ cout << "i = " << hex << i << " Base2::Print()" << endl; }
int i;
};
class Base1
{
public:
Base1(){ i = 0xB0; }
virtual void Print(){ cout << "i = " << hex << i << " Base2::Print()" << endl; }
int i;
};
class Base2{
public:
Base2(){ i = 0xC0; }
virtual void Print(){ cout << "i = " << hex << i << " Base2::Print()" << endl; }
int i;
};
class Derived :public Base0, public Base1, public Base2
{
public:
Derived(){ i = 0xD0; }
virtual void Print(){ cout << "i = " << hex << i << " Derived::Print()" << endl; }
int i;
};
void FunTest()
{
Derived derived;
cout << sizeof(derived) << endl;
Base0& base0 = derived;
base0.Print();
Base1& base1 = derived;
base1.Print();
Base2& base2 = derived;
base2.Print();
derived.Print();
}
由代码进行分析:
注:以上代码全是放在VS2013下进行调试的。