构造函数与虚析构函数

2024-03-07 19:48

本文主要是介绍构造函数与虚析构函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 构造函数
         因为在派生类中构造函数是不能继承的,也没有重定义的必要。在构造函数中调用虚函数将破坏动态绑定逻辑。下面的例子说明了这样动态绑定逻辑。
例:       
  #include<iostream.h>
  class base
   {
      protected:
      int x;
      public:
      base (int m)
       {
         x=m+1;
         print( );
       }
      virtual void print( )
       {
         cout<<”The virtual function  in base is called!”<<endl;
        cout<<x<<endl;
    }
};
class derive: public base
  {
     private:
     int y;
     public:
    derive (int m): base(m)
      {
        y=m;
        print( );
      }
virtual void print( )
    {
      cout<<”The virtual function in derive is called!”<<endl;
      cout<<y<<endl;
    }
 };
 void main( )
    {
      derive  obj(10);
    }
程序的输出结果为:
         The virtual function in base is called!
         11
         The virtual function in derive is called!
         10
         程序从创建派生类的对象开始执行,在执行中,先要调用基类的构造函数。此时派生类的创建过程尚未完成,只能按静态绑定调用基类的虚函数,无法实现预期的多态逻辑。
请记住:
          在构造和析构期间不要调用virtual 函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)。       
        如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。可以理解为:在base class 构造期间, virtual 函数不是virtual 函数。

2.虚析构函数

析构函数可以通过virtual修饰而声明为虚析构函数。虚析构函数与一般虚函数的不同之处在于:
(1)覆盖虚析构函数的重定义函数就是派生类的析构函数,不要求同名;
(2)虚析构函数由系统自动调用;
(3)一个虚析构函数的版本被调用后,接着就要调用被它覆盖的基类版本,依次类推,直到调用了派生序列的根类的析构函数版本为止。
     

    为什么析构函数为虚?

     在处理继承层次中的对象时,指针的静态类型可能与被删除对象的动态类型不同。因此基类中的析构函数必须为虚函数(虚函数特性会被继承,派生类的析构函数也是虚的)。

Base *pBase = new Derived;

delete pBase;

如果Base的析构函数不为虚,删除指针时只会执行Base的析构函数,造成内存泄漏;反之,则先析构Derived,再析构Base。

例:

#include<iostream.h>
 class base
  {
    public:
    virtual void say( )
     {
       cout<<”I am base.”<<endl;
     }
    ~base( )
     {
       cout<<”Delete object of
              base”<<endl;
     }
  };
class derived:public base
 {
   public:
   virtual void say( )
    {
      cout<<”I am derived”<<endl;
    }
  ~derived( )
    {
      cout<<”Delete object of derived”<<endl;
    };
void main( )
  {
    base *p1=new derived;
    p1->say( );
    delete p1;
  }
程序的输出结果为:
         I am derived
         Delete object of derived
        显然,p1->say( )语句正确地动态绑定了派生类中的重载代码,delete p1语句也正确地释放了为p1分配的内存空间。 但是,base*p1=new derived 语句执行时,先创建了一个指向base类对象的指针p1,然后又创建了一个derived类的动态对象,最后再让p1指向derived的对象。所以delete p1执行时应调用base和derived两个类的构造函数。
        由于该程序定义的多态性中没有将析构函数用virtual关键字说明, delete p1的执行是不正确的。 析构函数加上virtual 关键字说明,重新运行程序将得到以下正确结果:
         I am derived
         Delete object of derived
         Delete object of base

请记住:
• polymorphic (带多态性质的) base classes 应该声明一个virtual 析构函数。如果class 带有任何virtual 函数,它就应该拥有一个virtual 析构函数。
• Classes 的设计目的如果不是作为base classes 使用,或不是为了具备多态性(polymorphically) ,就不该声明virtual 析构函数。

这篇关于构造函数与虚析构函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/784622

相关文章

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

MySQL中COALESCE函数示例详解

《MySQL中COALESCE函数示例详解》COALESCE是一个功能强大且常用的SQL函数,主要用来处理NULL值和实现灵活的值选择策略,能够使查询逻辑更清晰、简洁,:本文主要介绍MySQL中C... 目录语法示例1. 替换 NULL 值2. 用于字段默认值3. 多列优先级4. 结合聚合函数注意事项总结C

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

golang panic 函数用法示例详解

《golangpanic函数用法示例详解》在Go语言中,panic用于触发不可恢复的错误,终止函数执行并逐层向上触发defer,最终若未被recover捕获,程序会崩溃,recover用于在def... 目录1. panic 的作用2. 基本用法3. recover 的使用规则4. 错误处理建议5. 常见错