赞
踩
virtual用来修饰父类函数中的普通函数,子类可以通过重写(与父类中的普通函数同名同参数)来覆盖父类中的函数的输出结果,即程序在运行时调用的是子类中的函数而不是父类。注意:在调用函数时(Barking()),我们用的父类的指针或引用指向子类对象。最终输出的结果是:

- #include <iostream>
- #include <string>
-
- using namespace std;
- class Animals
- {
- public:
- virtual void Bark()
- {
- cout << "动物叫声" << endl;
- }
- };
- class Cat :public Animals
- {
- public:
- virtual void Bark()
- {
- cout << "喵喵喵" << endl;
- }
- };
- void Barking(Animals& animals)
- {
- animals.Bark();
- }
- void text()
- {
- Cat cat;
- Barking(cat);
- }
-
- int main()
- {
- text();
- }

在解释纯虚函数之前,需要先明白一个问题,我们为什么要定义父类指针去指向子类对象,而不是直接用子类指针指向子类对象,下面这篇博客很好的解释了这个问题。父类引用指向子类对象_gideal_wang的博客-CSDN博客父类引用指向子类对象指的是:例如父类Animal,子类Cat,Dog。其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类。Animal animal = new Cat();即声明的是父类,实际指向的是子类的一个对象。 那这么使用的优点是什么,为什么要这么用?可以用这几个关键词来概括:多态、动态链接,向上转型也有人说这是面向接口编程,可以降低https://blog.csdn.net/gideal_wang/article/details/4913965?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166692105116782427487200%2522%252C%2522scm%2522%253A%252220140713.130212432.pc%255Fall.%2522%257D&request_id=166692105116782427487200&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~pc_ctr_v1-8-4913965-null-null.142%5Ev62%5Eopensearch_v2,201%5Ev3%5Eadd_ask,213%5Ev1%5Econtrol&utm_term=%E7%88%B6%E7%B1%BB%E6%8C%87%E9%92%88%E6%8C%87%E5%90%91%E5%AD%90%E7%B1%BB%E5%AF%B9%E8%B1%A1&spm=1018.2226.3001.4187 虚函数的输出结果可知,若所有子类中均重写了父类的一个虚函数,那么程序将不会调用父函数中的虚函数,这时,我们可以将这个父类中的虚函数定义成一个纯虚函数。
纯虚函数的定义方式:virtual 返回值类型 函数名(参数列表) = 0;
值得注意的是,当类中出现了纯虚函数,那么该类不能实例化对象;子类继承了父类中的纯虚函数而不是重写父类中的纯虚函数时,子类也不能实例化对象。
- #include <iostream>
- #include <string>
-
- using namespace std;
- class Animals
- {
- public:
- virtual void Bark() = 0;
- };
- class Cat :public Animals
- {
- public:
- virtual void Bark()
- {
- cout << "喵喵喵" << endl;
- }
- };
- void Barking(Animals& animals)
- {
- animals.Bark();
- }
- void text()
- {
- Cat cat;
- Barking(cat);
- }
-
- int main()
- {
- text();
- }

若不使用virtual修饰父类的析构函数,又使用了父类指针去指向子类对象,我们在最后delete实例化的对象(cat)的时候,系统不会调用Cat类的析构函数。运行结果:
- #include <iostream>
- #include <string>
-
- using namespace std;
- class Animals
- {
- public:
- virtual void Bark() = 0;
- Animals()
- {
- cout << "Animals的构造" << endl;
- }
- ~Animals()
- {
- cout << "Animals的析构" << endl;
- }
- };
- class Cat :public Animals
- {
- public:
-
- Cat()
- {
-
- cout << "Cat的构造" << endl;
- }
- virtual void Bark()
- {
- cout << "喵喵喵" << endl;
- }
- ~Cat()
- {
- cout << "Cat的析构" << endl;
- }
- };
-
- void text()
- {
- Animals* cat = new Cat;
- cat->Bark();
- delete cat;
-
- }
-
- int main()
- {
- text();
- }

若在使用Cat类的构造函数中new一个类(也就是自己申请一块地址,该参数存放在堆区,需要手动delete),而父类又不能调用子类的析构函数,这时我们需要用到virtual关键字去修饰父类的析构函数,从而达到手动释放堆区参数的目的。最终结果:

- #include <iostream>
- #include <string>
-
- using namespace std;
- class Animals
- {
- public:
- virtual void Bark() = 0;
- Animals()
- {
- cout << "Animals的构造" << endl;
- }
- virtual ~Animals()
- {
- cout << "Animals的析构" << endl;
- }
- };
- class Cat :public Animals
- {
- public:
- string* m_Name;
- Cat(string name)
- {
- m_Name = new string(name);
- cout << "Cat的构造" << endl;
- }
- virtual void Bark()
- {
- cout << *m_Name << "喵喵喵" << endl;
- }
- ~Cat()
- {
- if (m_Name != NULL)
- {
- delete m_Name;
- //delete一个指针后,编译器只是释放了指针中存放的地址中的内存空间,
- //但p中存放的地址还是原来的地址,
- //这时成为一个野指针,为了杜绝这种情况的发生,我们需要置NULL
- m_Name = NULL;
- }
- cout << "Cat的析构" << endl;
- }
- };
-
- void text()
- {
- Animals* cat = new Cat("Tom");
- cat->Bark();
- delete cat;
- }
-
- int main()
- {
- text();
- }

题外:为什么需要new一个类,而不是直接用最基础的参数定义方式,就是int number这种自动变量。其中一个原因是自动变量在离开作用域后回自动释放。
感觉没屁用。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。