关于 C++ 中多态 Polymorphism 的实现,我们都知道要通过虚函数 virtual function。记得我第一次接触多态的例程的时候,在父类 Base Class 中,并没有在需要子类 Derived Class 重定义的函数前声明 virtual,不过即便如此,也一样实现了多态。后来,我学习到 virtual function 的时候才发现,奇怪,不使用 virtual 也可以在子类中对函数进行重定义,那么 virtual 函数的不同在哪里呢?
讨论 1
C++ 的 non-virtual 函数可以重定义,那么 virtual 函数相比 non-virtual 函数有什么不一样?
结论:虚函数能让你在仅用一个指针进行调用时,确保访问到希望访问的方法,而非虚函数不行。
见如下 Code_1,基类的函数前并没有加 virtual。从输出结果可以看出,pa 指向 Pig 时候,并没有访问到 Pig 的方法。同样下面存放指针的 voa 容器也没有访问到正确的方法。
现在,我们把代码改成如下 Code_2(基类成员函数前加 virtual)。显而易见,现在都访问到了正确的方法。
不过需要注意的是,在这个例子中我并没有写析构函数 dtor,如果(在 Animal 中)写出则必须为 virtual。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <vector> using namespace std ;class Animal {public : void animalSound () { cout << "__Animal_make_a_sound__" << endl ; } }; class Pig : public Animal {public : void animalSound () { cout << "__Pig_make_a_sound__" << endl ; } }; class Dog : public Animal {public : void animalSound () { cout << "__Dog_make_a_sound__" << endl ; } }; int main (int argc, char **argv) { Animal _Animal; Pig _pig; Dog _dog; _Animal.animalSound(); _pig.animalSound(); _dog.animalSound(); Pig *pp = new Pig(); Animal *pa = pp; pa->animalSound(); pp->animalSound(); vector <Animal *> voa; voa.push_back(new Pig()); voa.push_back(new Dog()); for (auto x: voa) { x->animalSound(); cout << endl ; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <vector> using namespace std ;class Animal {public : virtual void animalSound () { cout << "__Animal_make_a_sound__" << endl ; } }; class Pig : public Animal {public : void animalSound () { cout << "__Pig_make_a_sound__" << endl ; } }; class Dog : public Animal {public : void animalSound () { cout << "__Dog_make_a_sound__" << endl ; } }; int main (int argc, char **argv) { Animal _Animal; Pig _pig; Dog _dog; _Animal.animalSound(); _pig.animalSound(); _dog.animalSound(); Pig *pp = new Pig(); Animal *pa = pp; pa->animalSound(); pp->animalSound(); vector <Animal *> voa; voa.push_back(new Pig()); voa.push_back(new Dog()); for (auto x: voa) { x->animalSound(); cout << endl ; } return 0 ; }
讨论 2
什么情况下使用 virtual ?或者说,有什么使用建议?
某类作为父类,其派生类需要对函数进行重定义 override,要对需要重定义的函数前加 virtual。
某类存在派生,那么它的析构函数 dtor 必须是 virtual 的,否则可能导致资源泄露。
non-virtual 函数可以确保当你使用父类指针时,一定能调用父类的方法(上述例子)。
另外,virtual 函数调用要通过虚表 virtual table 找到对应的函数指针,而 non-virtual 函数在编译的时候就已经确定了调用函数的地址。所以,调用函数的效率上 non-virtual 函数高一些。
讨论 3
⭐ 总结:
virtual 可以实现多态,这是我们都知道的。
non-virtual 也可以实现多态特性,但是在进行指针调用时候无法访问到子类的正确方法上,这一点需要注意。
所以,养成好习惯,父类需要重定义 override 的函数,就加上 virtual。