virtual用法一:多态

 #include<iostream>
using namespace std;
class A{
public:
virtual void display(){ cout<<"A"<<endl; }
};
class B : public A{
public:
void display(){ cout<<"B"<<endl; }
};
void doDisplay(A *p)
{
p->display();
delete p;
} int main(int argc,char* argv[])
{
doDisplay(new B());
return ;
}

这段代码打印出的结果为B,但是当把A类中的virtual去掉之后打印出的就为A。当基类中没有virtual的时候,编译器在编译的时候把p看做A类的对象,调用的自然就是A类的方法。但是加上virtual之后,将dispaly方法变成了虚方法,这样调用的时候编译器会看调用的究竟是谁的实例化对象,这样就实现了多态的效果。也就是说,当基类的派生类中有重写过基类的虚方法的时候,使用基类的指针指向派生类的对象,调用这个方法实际上调用的会是派生类最后实现的方法。

virtual用法二: 虚继承:解决数据冗余

 #include<iostream>
using namespace std;
class Person{
public: Person(){ cout<<"Person构造"<<endl; }
~Person(){ cout<<"Person析构"<<endl; }
};
class Teacher : virtual public Person{
public: Teacher(){ cout<<"Teacher构造"<<endl; }
~Teacher(){ out<<"Teacher析构"<<endl; }
};
class Student : virtual public Person{
public: Student(){ cout<<"Student构造"<<endl; }
~Student(){ cout<<"Student析构"<<endl; }
};
class TS : public Teacher, public Student{
public: TS(){ cout<<"TS构造"<<endl; }
~TS(){ cout<<"TS析构"<<endl; }
};
int main(int argc,char* argv[])
{
TS ts;
return ;
}

这段代码的终端输出结果为:

 Person构造
Teacher构造
Student构造
TS构造
TS析构
Student析构
Teacher析构
Person析构

当Teacher类和Student类没有虚继承Person类的时候,也就是把virtual去掉时候终端输出的结果为:

 Person构造
Teacher构造
Person构造
Student构造
TS构造
TS析构
Student析构
Person析构
Teacher析构
Person析构

大家可以很清楚的看到这个结果明显不是我们所期望的。我们在构造TS的时候需要先构造他的基类,也就是Teacher类和Student类。而Teacher类和Student类由都继承于Person类。这样就导致了构造TS的时候实例化了两个Person类。同样的道理,析构的时候也是析构了两次Person类,这是非常危险的,也就引发出了virtual的第三种用法,虚析构。

virtual用法三:虚析构:可以防止内存泄漏

 #include<iostream>
using namespace std;
class Person{
public: Person() {name = new char[];cout<<"Person构造"<<endl;}
virtual ~Person() {delete []name;cout<<"Person析构"<<endl;}
private:
char *name;
};
class Teacher :virtual public Person{
public: Teacher(){ cout<<"Teacher构造"<<endl; }
~Teacher(){ cout<<"Teacher析构"<<endl; }
};
class Student :virtual public Person{
public: Student(){ cout<<"Student构造"<<endl; }
~Student(){ cout<<"Student析构"<<endl; }
};
class TS : public Teacher,public Student{
public: TS(){ cout<<"TS构造"<<endl; }
~TS(){ cout<<"TS析构"<<ENDL; }
};
int main(int argc,char* argv[])
{
Person *p = new TS();
delete p;
return ;
}

这段代码的运行结果为:

 Person构造
Teacher构造
Student构造
TS构造
TS析构
Student析构
Teacher析构
Person析构

但是当我们把Person类中析构前面的virtual去掉之后的运行结果为:

 Person构造
Teacher构造
Student构造
TS构造
Person析构
程序崩溃

很明显这个结果不是我们想要的程序,崩溃造成的后果是不可预计的,所以我们一定要注意在基类的析构函数前面加上virtual,使其变成虚析构在C++程序中使用虚函数,虚继承和虚析构是很好的习惯 可以避免许多的问题。

原文:https://blog.csdn.net/jirryzhang/article/details/79392934

C++学习---- virtual的三种用法的更多相关文章

  1. virtual 三种用法

    virtual用法一 #include using namespace std;class A{public:     virtual  void  display(){  cout<<& ...

  2. using 的三种用法

    using 有哪三种用法? 1)引入命名空间. 2)给命名空间或者类型起别名. 3)划定作用域.自动释放资源,使用该方法的类型必须实现了 System.IDisposable接口,当对象脱离作用域之后 ...

  3. 学习Python的三种境界

    前言 王国维在<人间词话>中将读书分为了三种境界:"古今之成大事业.大学问者,必经过三种之境界:'昨夜西风凋碧树,独上高楼,望尽天涯路'.此第一境也.'衣带渐宽终不悔,为伊消得人 ...

  4. Js闭包常见三种用法

        Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...

  5. .NET(c#)new关键字的三种用法

    前几天去家公司面试,有一道这样的题:写出c#中new关键字的三种用法,思前想后挖空心思也只想出了两种用法,回来查了下msdn,还真是有第三种用法:用于在泛型声明中约束可能用作类型参数的参数的类型,这是 ...

  6. SQL、LINQ、Lambda 三种用法(转)

    SQL.LINQ.Lambda 三种用法颜色注释: SQL LinqToSql Lambda QA1. 查询Student表中的所有记录的Sname.Ssex和Class列.select sname, ...

  7. c# new关键字的三种用法

    三种用法如下: 在 C# 中,new 关键字可用作运算符.修饰符或约束. 1)new 运算符:用于创建对象和调用构造函数. 2)new 修饰符:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成 ...

  8. java中 this 的三种用法

    Java中this的三种用法 调用属性 (1)this可以调用本类中的任何成员变量 调用方法(可省略) (2)this调用本类中的成员方法(在main方法里面没有办法通过this调用) 调用构造方法 ...

  9. 子查询。ANY三种用法。ALL两种用法。HAVING中使用子查询。SELECT中使用子查询。

    子查询存在的意义是解决多表查询带来的性能问题. 子查询返回单行多列: ANY三种用法: ALL两种用法: HAVING中的子查询返回单行单列: SELECT中使用子查询:(了解就好,避免使用这种方法! ...

随机推荐

  1. Mybatis自动生成的BO对象继承公共父类(BO中过滤掉公共属性)

    使用mybatis的代码生成工具:mybatis-generator,如果自动生成的BO都有公共的属性,则可以指定这些BO继承父类(父类中定义公共属性) 1.定义父类 注意:属性public,不要使用 ...

  2. AngularJS 表格(带有CSS样式)

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  3. 任务学习-ucos

    1.任务(task)也称作一个线程: 2.一个任务有5种状态:休眠,就绪,运行,挂起,被中断 休眠:任务驻留在程序空间中,还没有交给ucos管理,把任务交给ucos 是通过调用OSTaskCreate ...

  4. sudo cd的错误

    问题说明 今天用MySQL建了库,想看看. 当到了这步,心里的第一个感觉就是电脑坏了.后来查了查才知道了原因. 原因 cd不是一个应用程序而是Linux内建的命令,而sudo仅仅只对应用程序起作用. ...

  5. IO Jar包

    密码e7ed https://pan.baidu.com/share/init?surl=LakzlwrjolWwpoft5j6aBg

  6. 细说多线程之Thread与Runnable

    1:创建线程的两种方式: 继承Thread类 public class MyThread extends Thread { @Override public void run() { } } MyTh ...

  7. JDK5后的特性整理

    为了大家对JDK有一个全面的了解,下面是我从网上查找并整理了JDK5以后的所有关键新特性!(将会持续更新中) JDK5新特性 自动装箱与拆箱 枚举 静态导入 可变参数(Varargs) 内省(intr ...

  8. 消费滚动滴log日志文件(flume监听,kafka消费,zookeeper协同)

    第一步:数据源 手写程序实现自动生成如下格式的日志文件: 15837312345,13737312345,2017-01-09 08:09:10,0360 打包放到服务器,使用如下命令执行,模拟持续不 ...

  9. Flask初学者:视图函数和类视图

    当一个url请求进入后台时,一般有两种方式来进行处理:视图函数和类视图.视图函数直接使用一个函数来进行处理并返回数据给浏览器,类视图则是使用类来进行处理并返回的,所以当需要进行的处理比较简单,则可以考 ...

  10. MariaDB数据库服务

    一.初始化mariaDB服务程序: yum install mariadb mariadb-server           //安装mariaDB systemctl start mariadb   ...