在同一类中不能定义两个名字相同、参数个数和类型都相同的函数,否则,将会重复定义;但是在类的继承层次中可以出现名字相同,参数列表相同而功能不同的函数,这是合法的,因为他们不在一个类中,但是,如果直接调编译系统将会按照同名覆盖的原则决定所调用的函数。例如:
class B
{
public:
void test()
{
cout << "B::test()" << endl;
}
};
class D :public B
{
public:
void test()
{
cout << "D::test()" << endl;
}
};
int main()
{
D d;
d.test();
}
结果:D::test()
在派生类中调用test()函数,将按照同名隐藏,优先访问派生类中的函数,这时如果要想访问继承下来的基类中的元素,可以添加作用域标识符:如d::B.test()。但是这样做很不方便,当程序比较大,很容易搞混。
鉴于以上的缺陷,人们用将基类中同名函数声明为虚函数,添加关键字virtual,通过同一个形式来达到不同的目的,既能调用派生类又能调用基类的同名函数,但是这种调用形式是用基类类型的指针或引用来调用的,例如将上代码稍加修改:
class B
{
public:
virtual void test() //声明为虚函数
{
cout << "B::test()" << endl;
}
};
class D :public B
{
public:
virtual void test()
{
cout << "D::test()" << endl;
}
};
int main()
{
B*pB1 = new B;
pB1->test();
B*pB2 = new D;
pB2->test();
return 0;
}
此时输出:
B::test()
D::test()
说明:基类指针是指向基类对象的,如果用它指向派生类对象,则进行指针类型转换,将派生类型对象的指针先转换为基类的指针,所以基类指针指向的是派生类中基类部分,在程序修改前,是无法用基类指针去调用派生类对象的成员函数。
基类声明为虚函数后,派生类中的虚函数取代了基类原来的虚函数,因此使基类指针指向派生类对象后,调用虚函数就调用了派生类中的虚函数。。。
因此,在使用时,把基类中的某个成员声明为虚函数后,允许其在派生类中重新定义,赋予它新的功能,并且可以通过指向基类的指针或指向不同类的对象,从而调用其中的同名函数。
虚函数的使用方法:
1)、在基类中用virtual声明成员函数为虚函数,这样就可在派生类中重新定义次函数,为它赋予新的功能。
2)、在派生类中重新定义定义次函数时,要求函数名、函数类型、函数参数列表和类型都必须与基类的虚函数相同,然后根据派生类的需要重新定义函数体。
3)、定义一个指向基类的指针变量,并使他指向同一类族中需要调用该函数的对象。
4)、通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
注:c++规定,当一个成员函数被声明为虚函数后,其派生类的同名函数自动成为虚函数,因此,在派生类中重新声明该函数时,可以加virtual,也可以不加,但是习惯上添加,为了保持程序逻辑更加清晰。。
函数重载和虚函数区别:
1、函数重载可以用于非成员函数和类的成员函数,而虚函数只能用于类的成员函数
2、函数重载是处理同一层次上横向的同名函数。
3、虚函数是处理不同层次纵向上的同名函数。
4、重载的函数必须具有相同的函数名,函数类型可以相同也可以不同,但函数的参数个数和参数类型二者中至少有一个不同,否则在编译时无法区分。而虚函数则要求同一类族中的所有虚函数的函数名,函数类型,函数的参数个数和参数类型都全部相同,否则就不是重定义了,也就不是虚函数了
5、函数重载是在程序编译阶段确定操作的对象的,属于静态关联。虚函数是在程序运行阶段确定操作对象的,属于动态关联。
纯虚函数
在成员函数的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
class B
{
virtual void test() = 0;
};
int main()
{
B b;
return 0;
}
1)构造函数不能声明为虚函数
构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。
2)静态函数不能给成虚函数
静态函数不是类的成员函数,而虚函数只是针对类的成员函数。
3)不建议将赋值运算符给成虚函数
对于赋值操作符,虽然可以在基类中将成员函数operator=定义成虚函数,但这样做并不会影响派生类中赋值操作符的使用。因为每个类有自己的赋值操作符。每个类的赋值操作符都有一个和类本身类型相同的形参,该类型必须不同于继承层次中任意其他类的赋值操作符的形参类型。因此子类的赋值操作符和基类的赋值操作符并不是同一个。但是,在这个子类中仍然有基类的那个操作符,但不是赋值操作符。将赋值操作符设为虚函数容易让人混淆,因为虚函数必须在基类和派生类中具有相同的形参,基类赋值操作符有一个形参是自身类类型的引用,如果该操作符为虚函数,则每个类都将得到一个虚函数成员,该成员定义了参数为一个基类对象的operator=。但是,对于派生类而言,这个操作符与赋值操作符是不同的。因此,将赋值操作符设为虚函数很容易令人混淆,并且没有什么用处。