精华内容
下载资源
问答
  • C++ 虚拟析构函数 (virtual destructor)

    千次阅读 多人点赞 2021-05-15 07:19:47
    C++ 虚拟析构函数 (virtual destructor). 虚拟析构函数的用法以及使用场景.

    C++ 虚拟析构函数

    概述

    虚析构函数 (virtual destructor) 可以帮我们实现基类指针删除派生类对象.

    在这里插入图片描述

    问题

    当我们从派生类的对象从内存中撤销时会先调用派生的析构函数, 然后再基类的析构函数, 由此就会产生问题:

    • 如果用 new 运算符建立了派生类对象, 并且由一个基类的指针比那里指向该对象
    • 用 delete 运算符撤销对象时, 系统只执行基类的析构函数. 而不执行派生类的析构函数, 派生类对象析构中要求的工作将被忽略

    Base 类:

    #ifndef PROJECT6_BASE_H
    #define PROJECT6_BASE_H
    
    #include <iostream>
    using namespace std;
    
    class Base {
    public:
        Base() {
            cout << "执行基类构造函数" << endl;
        };
        ~Base() {
            cout << "执行基类析构函数" << endl;
        };
    };
    
    #endif //PROJECT6_BASE_H
    

    Derived 类:

    #ifndef PROJECT6_DERIVED_H
    #define PROJECT6_DERIVED_H
    
    #include <iostream>
    #include "Base.h"
    using namespace std;
    
    class Derived : public Base {
    public:
        Derived() {
            cout << "执行派生类构造函数" << endl;
        };
        ~Derived() {
            cout << "执行派生类析构函数" << endl;
        }
    };
    
    #endif //PROJECT6_DERIVED_H
    

    main:

    #include <iostream>
    #include "Derived.h"
    using namespace std;
    
    int main() {
    
        Base *pt =new Derived;
        delete pt;
    
        return 0;
    }
    

    输出结果:

    执行基类构造函数
    执行派生类构造函数
    执行基类析构函数
    

    虚析构函数

    当基类的析构函数为虚函数时, 无论指针指的是同一族中的哪一个类对象, 系统会采用动态关联, 掉啊用相应的析构函数, 对该对象进行清理工作. 即先调用了派生类的析构函数, 再调用了基类的析构函数.

    Base 类:

    #ifndef PROJECT6_BASE_H
    #define PROJECT6_BASE_H
    
    #include <iostream>
    using namespace std;
    
    class Base {
    public:
        Base() {
            cout << "执行基类构造函数" << endl;
        };
        virtual ~Base() {
            cout << "执行基类析构函数" << endl;
        };
    };
    
    #endif //PROJECT6_BASE_H
    

    Derived 类:

    #ifndef PROJECT6_DERIVED_H
    #define PROJECT6_DERIVED_H
    
    #include <iostream>
    #include "Base.h"
    using namespace std;
    
    class Derived : public Base {
    public:
        Derived() {
            cout << "执行派生类构造函数" << endl;
        };
        ~Derived() {
            cout << "执行派生类析构函数" << endl;
        }
    };
    
    #endif //PROJECT6_DERIVED_H
    

    main:

    #include <iostream>
    #include "Derived.h"
    using namespace std;
    
    int main() {
    
        Base *pt =new Derived;
        delete pt;
    
        return 0;
    }
    

    输出结果:

    执行基类构造函数
    执行派生类构造函数
    执行派生类析构函数
    执行基类析构函数
    

    总结

    如果将基类的析构函数声明为虚函数时, 由该基类所派生的所有派生类的析构函数也都自动成为虚函数. 即使派生类的析构函数与其基类的构造函数名字不相同.

    最好把基类的析构函数声明为虚函数. 即使基类并不需要析构函数, 我们也可以定义一个函数体为空的虚析构函数, 以保证撤销动态分配空间能正确的处理.

    注: 构造函数不能声明为虚函数.

    展开全文
  • ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" ; }; void DoSomething() { cout !" ; };};  代码 ClxBase *pTest = new ClxDerived;pTest->DoSomething();delete pTest; ...

    主要内容:

    1、C++类继承中的构造函数和析构函数

    2、C++多态性中的静态绑定和动态绑定

    3、C++多态性中析构函数声明为虚函数

     

    1、C++类继承中的构造函数和析构函数

    在C++的类继承中,

    建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推;

    析构对象时,其顺序正好与构造相反;

    具体参考文章:http://www.cnblogs.com/AndyJee/p/4575385.html

     

    2、C++多态性中的静态绑定和动态绑定

    对象的静态类型:对象在声明是采用的类型,在编译期确定;

    对象的动态类型:当前对象所指的类型,在运行期决定,对象的动态类型可以更改,但静态类型无法更改。

    静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
    动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。

    具体参考文章:http://www.cnblogs.com/AndyJee/p/4575670.html

     

    3、C++多态性中基类析构函数声明为虚函数

    先来看几段程序例子:

    • 将基类析构函数声明为虚函数

    ?

    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

    #include <iostream>

    using namespace std;

     

     

    class Person{

    public:

        virtual ~Person(){  //declare destructor as a virtual function

        cout << "Person::~Person()" << endl;

        }

    };

     

    class Student : public Person{

    public:

        ~Student(){     // virtual or not is OK

            cout << "Student::~Student()" << endl;

        }

    };

     

    int main(){

        Person *pt1 = new Person;

        Person *pt2 = new Student;        // base class pointer point to derived class

        // Student *pt3 = new Person;     // derived class pointer can not point to base class

        Student *pt4 = new Student;

     

        delete pt1;

        cout << "*********" << endl;

        delete pt2;

        cout << "*********" << endl;

        //delete pt3;

        //cout << "*********" << endl;

        delete pt4;

        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

    #include <iostream>

    using namespace std;

     

     

    class Person{

    public:

        ~Person(){  //declare destructor as a virtual function

        cout << "Person::~Person()" << endl;

        }

    };

     

    class Student : public Person{

    public:

        ~Student(){     // virtual or not is OK

            cout << "Student::~Student()" << endl;

        }

    };

     

    int main(){

        Person *pt1 = new Person;

        Person *pt2 = new Student;        // base class pointer point to derived class

        // Student *pt3 = new Person;     // derived class pointer can not point to base class

        Student *pt4 = new Student;

     

        delete pt1;

        cout << "*********" << endl;

        delete pt2;

        cout << "*********" << endl;

        //delete pt3;

        //cout << "*********" << endl;

        delete pt4;

        cout << "*********" << endl;

     

        return 0;

    }

    运行结果:

     

    可以看出:

    在用基类指针指向派生类时,

    在基类析构函数声明为virtual的时候,delete基类指针,会先调用派生类的析构函数,再调用基类的析构函数。

    在基类析构函数没有声明为virtual的时候,delete基类指针,只会调用基类的析构函数,而不会调用派生类的析构函数,这样会造成销毁对象的不完全。

    分析:

    Person *pt2 = new Student;

    pt2的静态类型为Person,而动态类型为Student,

    当析构函数为虚函数时,为动态绑定,delete pt2,会调用动态类型即派生类的析构函数,由于继承关系,也会调用基类的析构函数;

    而当析构函数为非虚函数时,为静态绑定,delete pt2,会调用静态类型即基类的析构函数,而不会调用派生类的析构函数。

    (以上纯属个人理解)

     

    总结:

    • 应该为多态基类声明虚析构器。一旦一个类包含虚函数,它就应该包含一个虚析构器,因为多态性,必定会有基类调用派生类。

    • 如果一个类不用作基类或者不需具有多态性,便不应该为它声明虚析构器。

     

    参考文章:

    http://www.cnblogs.com/children/archive/2012/08/13/2636956.html

     

    另一篇:

    我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:        有下面的两个类:

    class ClxBase{public:    ClxBase() {};    virtual ~ClxBase() {};    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };};class ClxDerived : public ClxBase{public:    ClxDerived() {};    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };     void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };};

        代码

    ClxBase *pTest = new ClxDerived;pTest->DoSomething();delete pTest;

        的输出结果是:
    Do something in class ClxDerived!Output from the destructor of class ClxDerived!
        这个很简单,非常好理解。    但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
    Do something in class ClxDerived!
        也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。    所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。    当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
    ————————————————
    版权声明:本文为CSDN博主「StarLee」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/starlee/article/details/619827

    展开全文
  • GCC可以给函数若干属性,其中constructor就是其中一个。具体有哪些属性,可以看GCC的文档。 公共属性:... ... 在上面文档中有对于constructor与destructor的描述: ..

    GCC可以给函数若干属性,其中constructor就是其中一个。具体有哪些属性,可以看GCC的文档。

    公共属性:https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

    所有属性:http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html 

    在上面文档中有对于constructor与destructor的描述: 

    引用

    constructor

    destructor

    constructor (priority)

    destructor (priority)

    The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () completes or exit () is called. Functions with these attributes are useful for initializing data that is used implicitly during the execution of the program.

    On some targets the attributes also accept an integer argument to specify a priority to control the order in which constructor and destructor functions are run. A constructor with a smaller priority number runs before a constructor with a larger priority number; the opposite relationship holds for destructors. So, if you have a constructor that allocates a resource and a destructor that deallocates the same resource, both functions typically have the same priority. The priorities for constructor and destructor functions are the same as those specified for namespace-scope C++ objects (see C++ Attributes). However, at present, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute constructor are invoked is unspecified. In mixed declarations, attribute init_priority can be used to impose a specific ordering.

    Using the argument forms of the constructor and destructor attributes on targets where the feature is not supported is rejected with an error.


            大致意思就是,可以给一个函数赋予constructor或destructor,其中constructor在main开始运行之前被调用,destructor在main函数结束后被调用。如果有多个constructor或destructor,可以给每个constructor或destructor赋予优先级,对于constructor,优先级数值越小,运行越早(优先级 0 到 100 为实现所保留,所以最小为101)。destructor则相反,优先级数值越小,运行越晚(优先级 0 到 100 为实现所保留,所以最小为101)。 
    下面是一个例子: 

    C代码  收藏代码

    #include <stdio.h>
     
    void begin(void) __attribute__((destructor(103)));
    void end(void) __attribute__((destructor(102)));
     
    int main(void)
    {
            printf ("\nInside main()\n");
    
            return 0;
    }
    
    void begin(void)
    {
            printf ("\nIn begin()\n");
    }
    
    void end(void)
    {
            printf ("\nIn end()\n");
    }
    


       
      运行的结果为: 

    Inside main()

    In begin()

    In end()

    reference:https://blog.csdn.net/xiaofei125145/article/details/52597256

    展开全文
  • Cookie DestrucTor-开源

    2021-05-03 16:37:31
    Cookie DestructoR可帮助您维护网络隐私。 该软件具有即时清除Cookie和Internet缓存的功能。 如有必要,可以销毁计算机上的Index.dat数据库文件。
  • C++中的trivial destructor 转:http://blog.csdn.net/wudishine/article/details/12307611   如果用户不定义析构函数,而是系统自带的,那么说明析构函数基本没什么用(但默认会被调用)。我们称之为trivial ...

    C++中的trivial destructor
    转:http://blog.csdn.net/wudishine/article/details/12307611
      如果用户不定义析构函数,而是系统自带的,那么说明析构函数基本没什么用(但默认会被调用)。我们称之为trivial destructor。反之,如果特别定义了析构函数,则说明需要在释放空间之前做一些事情,则这个析构函数称之为non-trivial destructor。如果某个类中只有基本类型的话是没有必要调用析构函数的,delete的时候基本不会产生析构代码。
      在C++的类中如果只有基本的数据类型,也就不需要写显式的析构函数,即用默认析构函数就够用了,但是如果类中有个指向其他类的指针,并且在构造时候分配了新的空间,则在析构函数中必须显式释放这块空间,否则会产生内存泄露。
      在STL中空间配置时候destory()函数会判断要释放的迭代器的指向的对象有没有 trivial destructor(STL中有一个 has_trivial_destructor函数,很容易实现检测)放,如果有trivial destructor则什么都不做,如果没有即需要执行一些操作,则执行真正的destory函数。destory()有两个版本,第一个版本接受一个指针,准备将该指针所指之物析构掉,第二个版本接受first和last两个迭代器,准备将[first,last]范围内的所有对象析构掉。我们不知道这个范围有多大,万一很大,而每个对象的析构函数都无关痛痒,那么一次次调用这些析构函数,对效率是一种伤害,因此这里首先利用value_type()获得迭代器所指对象的类别,再利用_type_traits判断该型别的析构函数是否无关痛痒,若是(_true_type),则什么也不做就结束,若否(_false_type),这才以循环的方式巡访整个范围,并在循环中每经历一个对象就调用第一个版本的destory()。

    展开全文
  • 在使用vs调试时,析构函数执行时在’scalar deleting destructor’的位置报错,怀疑是指针被delete了多次,但最后发现是”被释放的内存存在越界问题“,比较难发现。问题简要如下 class MemCorrupt { public: ...
  • game-destructor:由GitHub Classroom创建的game-destructor
  • Computer Destructor-开源

    2021-06-08 17:42:00
    这是你应该完全拥有它的最棒的 pwogwam eva! 啊啊啊啊
  • gem 'destructor' 然后执行: $ bundle 或者自己安装: $ gem install destructor 用法 require 'destructor' class Foo attr_reader :bar def initialize @bar = 123 end def finalize puts ...
  • 因此,destructor 关注的首要问题就是内存的操作,不能越界销毁或重复销毁,也不能销毁不彻底,造成内存泄漏。 最佳实践 Best Practice 存在继承的情况下,base class 的 destructor 就声明为 virtual , 防止析构 ...
  • /* constructor和destructor不可以调用virtual函数 constructor: 因为构造derived类的对象的时候首先调用base类的constructor, 而base类里调用virtual函数是调用base类里的实现,这可能和预期想调用derived类中的...
  • 今天写代码是遇到这样一个问题error: expected constructor, destructor, or type conversion before '.' token;立马网上查,原来是说不能再全局域进行不能用于赋值、运算、调用函数等,只能做变量的声明和初始化...
  • Bison之destructor

    2019-11-01 10:43:09
    因为我一开始以为,在每次销毁token的时候都会调用这个destructor。因为我的token有的会带有指针类型的值,但却并不是所有匹配规则都会用到这个指针指向的值。所以我以为只要设置了destructor,在每次匹配成功一个...
  • 关于 Dynamic atexit destructor for ***

    千次阅读 2019-03-15 17:27:33
    Dynamic atexit destructor for xxx 一般遇到这个 call stack 很可能是全局变量析构函数中有依赖导致的。这个 call stack 会很短(因为有可能是任何情况导致程序退出),直接分析可能找不出原因。 至于全局变量的...
  • C++中的__has_trivial_destructor(typename) 最近学习《STL源码剖析》,实际看了stl头文件stl_construct.h,文件中看到有如下代码: template<typename _ForwardIterator> inline void _Destroy(_...
  • 析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号。 ...
  • warning: delete called on non-final 'xxx' that has virtual functions but non-virtual destructor 因为类中有纯虚函数。 只要纯虚函数, 就必须要虚析构函数。 所以解决办法就是在原类中定义虚析构函数。 ...
  • An alternate way to flag a function as a C constructor or destructor can also be done at the time of the function definition. 1 2 3 4 5 6 7 8 __attribute__((constructor))...
  • 如果用户不定义析构函数,而是用系统自带的,则说明,析构函数基本没有什么用(但默认会被调用)我们称之为trivial destructor。反之,如果特定定义了析构函数,则说明需要在释放空间之前做一些事情,则这个析构函数...
  • 写生成随机数的时候报错,其实只要把随机函数写在main函数里面即可。
  • 丑如罪 的mod,添加了各种可选功能和机制。 最初以HD-Scavenger开头,专注于长期的“生存”功能,但后来Swift扩展为通用增强型mutator。 特征 通常将功能实现为具有各种可自定义设置的可切换模块。...
  • 报错:error: expected constructor, destructor, or type conversion before '*' token class AVLTree { private: struct BTNode { int key; struct BTNode *left; struct BTNode *r...
  • c++中的 trivial destructor

    2020-07-08 17:31:03
    如果用户不定义析构函数,而是用系统自带的,则说明,析构函数基本没有什么用(但默认会被调用)我们称之为trivial destructor。反之,如果特定定义了析构函数,则说明需要在释放空间之前做一些事情,则这个析构函数...
  • 这是一个编译错误,其含意是:在字符 ‘(’ 之前,应该是一个构造函数、析构函数或是类型转换等标识。编译程序现在在'('之前缺少必要的标识符,故提示错误。给你一个例子: int *p; p = new (10);...
  • C++中的析构函数(destructor

    千次阅读 2018-08-28 14:25:36
    和构造函数相似,析构函数也是一个特殊的类方法,它是用来自动释放对象的。析构函数的表示形式为:在类名前面加上“~”即表示析构函数。与构造函数不同的是,一个类中只允许一个析构函数存在。 ...
  • attribute((destructor)) 在main()函数后调用 #include <stdio.h> #include <stdlib.h> static void before(void) __attribute__((constructor)); static void after(void) __attribute__((destructor)...
  • 原文请参考 ... constructor和destructor概述(c++ only) 对象的初始化和清除比简单的数据结构复杂的多,这是因为类有比较复杂的内部
  • 2,3)]) df.to_excel(xlsx, sheet_name='a', index=False) 在for循环外再次加了一个写入excel的操作,就会报错:Exception caught in workbook destructor. Explicit close() may be require。 这个报错一般是两个...
  • /home/star/rikirobot/catkin_ws/src/depth_camera/depthimage_to_laserscan/src/DepthImageToLaserScanNodelet.cpp:60:24: error: expected constructor, destructor, or type conversion before ‘(’ token ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,351
精华内容 16,540
关键字:

destructor