精华内容
下载资源
问答
  • C++的析构函数调用和虚析构函数

    千次阅读 2013-11-13 08:48:40
    析构函数一般是自动调用的,但是某些情况下页需要显式调用,如出栈和入栈操作: void C::push_back(const X& a) { // ... new(p) X{a}; // copy constr uct an X with the value a in address p // ... }   void C:...

    析构函数一般是自动调用的,但是某些情况下页需要显式调用,如出栈和入栈操作:

    void C::push_back(const X& a)
    {
    // ...
    new(p) X{a}; // copy constr uct an X with the value a in address p
    // ...
    }

     

    void C::pop_back()
    {
    // ...
    p−>˜X(); // destroy the X in address p
    }


    也可以创建一个类,只能是显式调用析构函数,而不能隐式调用,如下:

    class Nonlocal {
    public:
    // ...
    void destroy() { this−>˜Nonlocal(); } // explicit destruction
    private:
    // ...
    ˜Nonlocal(); //don’t destroy implicitly
    };
    void user()
    {
    Nonlocal x; // error : cannot destroy a Nonlocal
    X∗ p = new Nonlocal; // OK
    // ...
    delete p; // error : cannot destroy a Nonlocal
    p.destroy(); //OK
    }

    这个时候,析构函数私有化了,不能被外部调用。只能调用公有函数destroy()

     

    虚析构函数,就是在虚构函数前面增加virtual关键字,通常用在有其他虚函数的类中。

    class Shape {
    public:
    // ...
    virtual void draw() = 0;
    virtual ˜Shape();
    };
    class Circle {
    public:
    // ...
    void draw();
    ˜Circle(); // overrides ˜Shape()
    // ...
    };

    虚析构函数的作用是当继承的时候,可以调用正确的析构函数正确地释放资源。

    如下面Shape *p其实是可以想象为作为一群派生类的接口,当调用p->draw()的时候,就会调用适当的派生类如Circle, triangle等。

    这个时候调用析构函数的时候也需要利用虚函数机制,才能调用适当的虚函数,释放适当的资源。

    void user(Shape∗*p)
    {
    p−>draw(); // invoke the appropriate draw()
    // ...
    delete p; // invoke the appropriate destructor
    };

    某些书,如钱能的C++程序设计讲某些问题实在是讲的不好懂。讲的太绕了,无需深入去看。因为讲得并不好,也不实用。

    其实就简单记得,所有基类的析构函数都记得定义为虚析构函数,前面加virtual。问题就解决了。

    展开全文
  • 虚函数被的构造析构函数和成员函数调用虚函数的执行过程,需要的朋友可以参考下
  • 一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。 简单来说,其构造函数...
  • 不论基类的析构函数是否为virtual的,派生的对象过期时都是先调用自己析构函数,然后再调用基类的析构函数。 另外: virtual的作用是“让基类能够正确调用派生的函数” 所以派生自己(不管是对象还是指针)...

    不论基类的析构函数是否为virtual的,派生类的对象在过期时都是先调用自己析构函数,然后再调用基类的析构函数。

    另外:

    virtual的作用是“让基类能够正确调用派生类的函数”

    所以派生类自己(不管是对象还是指针)的析构根本不受virtual的影响,永远是“先调用自己的析构函数再调用基类的析构函数”

    析构函数使用virtual的作用是“当使用基类指针指向派生类的时候,delete该指针可以正确调用派生类的析构函数”

    class A
    {
    public:
      A(){};
      ~A(){}
    };
    class B : public A
    {
    public:
      B(){};
      ~B(){};
    };
     
    如果你的代码只是:
    B *b = new B; delete b;那是肯定先调B的析构,然后调A的析构
    但是如果是这样的多态形式
    A *a = new B; delete a; 这个时候的析构只调用了基类A的析构,如果把A的析构函数定义成virtual的,那就会先调B的析构,然后调A的,所以涉及到继承的,最好把父类的析构函数定义成vritual形式


    C++ 派生类的析构函数的调用顺序为:
    A)  基类、派生类和对象成员类的析构函数
     B)  派生类、对象成员类和基类的析构函数
     C)  对象成员类、派生类和基类的析构函数   
     D)  派生类、基类和对象成员类的析构函数
    答案是选着B,

    展开全文
  • C++有一个很重要的法则:使用构造函数创建对象的顺序与使用析构函数释放对象的顺序相反。对于一些C++的初学者来说,这是一条有点费解的法则,那么该怎么理解和清晰的表现出来呢?下面我们通过程序来体现一下: #...

           在C++中有一个很重要的法则:使用构造函数创建对象的顺序与使用析构函数释放对象的顺序相反。对于一些C++的初学者来说,这是一条有点费解的法则,那么该怎么理解和清晰的表现出来呢?下面我们通过程序来体现一下:

    #include<iostream>
    using namespace std;
    
    //创建一个汽车类
    class Car
    {
    public:
            //构造函数
    	Car(short, int);
            //析构函数
    	~Car();
    	void move();
    private:
    	short speed;
    	int num;
    };
    
    Car::Car(short s, int n)
    {
    	speed = s;
    	num = n;
    	cout << "创建第" << num << "辆车,速度是" << speed << " 米/秒" << endl;
    }
    
    Car::~Car()
    {
    	cout << "销毁掉第 " << num << "辆车" << endl;
    }
    
    void Car::move()
    {
    	cout << "第 " << num << "辆车速度是" << speed << endl;
    }
    
    //主函数
    void main()
    {
            //先创建第car1对象,再创建car2对象
    	Car car1(10, 1), car2(20, 2);
    	car1.move();
    	car2.move();
    }

           编译执行的结果如下(编译环境为Visual Studio 2013):


           分析输出结果,我们可以看到,在主函数中是首先创建了car1类,后创建了car2类,所以在执行构造函数的顺序上,是先执行car1对象的构造函数,后执行car2对象的构造函数。而在主函数执行结束,要调用析构函数的时候,根据开始说的法则,则会先执行car2类的析构函数,后执行car1类的析构函数。这就是“使用构造函数创建对象的顺序与使用析构函数释放对象的顺序相反”这条法则的一个很好的体现

           那么我们可以举一反三,如果想先执行car1类的析构函数,后执行car2类的析构函数,那么上述程序该如何修改呢?很简单,依据上述法则,我们只需要修改为先创建car2类,后创建car1类,就可以了。修改后的程序如下:

    #include<iostream>
    using namespace std;
    
    class Car
    {
    public:
    	Car(short, int);
    	~Car();
    	void move();
    private:
    	short speed;
    	int num;
    };
    
    Car::Car(short s, int n)
    {
    	speed = s;
    	num = n;
    	cout << "创建第" << num << "辆车,速度是" << speed << " 米/秒" << endl;
    }
    
    Car::~Car()
    {
    	cout << "销毁掉第 " << num << "辆车" << endl;
    }
    
    void Car::move()
    {
    	cout << "第 " << num << "辆车速度是" << speed << endl;
    }
    
    void main()
    {
            //这次先创建car2类,后创建car1类
    	Car car2(20, 2), car1(10, 1);
    	car1.move();
    	car2.move();
    }
           编译执行的结果如下:

           这次我们看到,由于是先创建了car2类,后创建了car1类,所以先执行了car2类的构造函数,后执行car1类的构造函数;而在最后,会先执行car1类的析构函数,后执行car2类的西沟函数。

           当然。我们还可以再往深了想一想,上面这两个例子都是基于同一个类来创建两个对象的,那么如果是两个不同的类创建的对象呢?是否依然符合这条法则呢?我们再修改程序来验证一下:

    #include<iostream>
    using namespace std;
    
    //创建一个汽车类
    class Car
    {
    public:
    	Car(short, int);
    	~Car();
    	void move();
    private:
    	short speed;
    	int num;
    };
    
    //创建一个司机类
    class Driver
    {
    public:
    	Driver(short);
    	~Driver();
    	void drive();
    private:
    	short years;
    };
    
    Car::Car(short s, int n)
    {
    	speed = s;
    	num = n;
    	cout << "创建第" << num << "辆车,速度是" << speed << " 米/秒" << endl;
    }
    
    Car::~Car()
    {
    	cout << "销毁掉第 " << num << "辆车" << endl;
    }
    
    void Car::move()
    {
    	cout << "第 " << num << "辆车速度是" << speed << endl;
    }
    
    Driver::Driver(short y)
    {
    	cout << "我是一个" << y << "岁的司机" << endl;
    }
    
    Driver::~Driver()
    {
    	cout << "我要停车了" << endl;
    }
    
    void Driver::drive()
    {
    	cout << "我要开车了" << endl;
    }
    
    //主函数
    void main()
    {
            //先创建汽车类
    	Car car1(10, 1);
            //后创建司机类
    	Driver driver1(30);
    	driver1.drive();
    	car1.move();
    }
           编译执行的结果如下:

           通过输出结果,我们可以看到,先执行了Car1类的构造函数,后执行了Driver1类的构造函数,而在最后,则是先执行了Driver1类的析构函数,后执行了Car1类的析构函数,也就是说,不同的类依然是符合上述法则的

           上述例子较为简单,而在实际应用中,主函数中可能远比上述程序复杂的多,可能会执行更多的类操作,这样就需要我们去时刻注意一下这条法则,避免出现一些看起来很“莫名其妙”的错误。



    展开全文
  • c++中析构函数会被调用几次,什么时候调用调用次数和什么有关
  • C++继承构造函数、析构函数调用顺序及虚析构函数 首先说说构造函数,大家都知道构造函数里就可以调用成员变量,而继承中子是把基类的成员变成自己的成员,那么也就是说子类构造函数里就可以调用...

    C++继承中构造函数、析构函数调用顺序及虚析构函数

    首先说说构造函数,大家都知道构造函数里就可以调用成员变量,而继承中子类是把基类的成员变成自己的成员,那么也就是说子类在构造函数里就可以调用基类的成员了,这就说明创建子类的时候必须先调用基类的构造函数,只有这样子类才能在构造函数里使用基类的成员,所以是创建子类时先调用基类的构造函数然后再调用自己的构造函数。通俗点说,你要用某些物品,但这些物品你没办法自己生产,自然就要等别人生产出来,你才能拿来用。

    接着就是析构函数了,上面说到子类是将基类的成员变成自己的成员,那么基类就会只存在子类中直到子类调用析构函数后。做个假设:假如在基类的析构函数调用比子类的先,这样会发生什么事呢?类成员终止了,而类本身却还在,但是在类存在的情况下,类成员就应该还存在的,这不就产生矛盾了吗?所以子类是调用自身的析构函数再调用基类的析构函数。基类的析构函数必须设置为虚的,而作为最终子类则可以是虚的也可以不是虚的,因为没有其他类继承于它不会影响最终功能。但又不是所有类的析构函数都设置为虚的比较好,因为存在虚函数的类实例化时会额外添加一个虚表指针,浪费内存性能。

    现在到了虚函数了,virtual主要作用是在多态方面,而C++的多态最主要的是类的动态绑定,动态绑定则是指将子类的指针或引用转换成基类,基类对象就可以动态判断调用哪个子类成员函数。这就说明在没有子类指针或引用转换为基类对象的话,virtual没有存在意义(纯虚函数除外),也就是有没有virtual都是调用其自身的成员函数。通过这些分析,对于virtual就有了眉目了。当子类指针或引用转换为基类时,若基类中有用virtual定义的函数,被子类重写后,此基类对象就会根据子类调用子类中的重写后的函数,而不是基类中的函数;反之,若是基类中没有用virtual定义,则不管基类被赋值的是哪个子类的值,调用的都是基类的成员函数(当然指的值子类重载的基类函数,不然就算要调用子类特有的成员函数也会编译不过)。

    存在虚析构函数为什么不会存在虚构造函数呢?

    构造函数不能是虚函数,因为构造子类时本身也是调用的子类构造函数,然后子类构造函数会调用基类构造函数,所以虚构造函数的存在是没有意义的。只有在构造完成后,对象才能成为一个类的名符其实的实例。另外,静态成员函数和内联函数也不能是虚函数。

    虚函数是针对对象的,不是针对类的.

    这一点可以从类成员函数(即静态成员函数)不能是虚函数看出来.倘若类不被实例化为对象,虚函数的存在本身也没意义.


    上面的假设我感觉并不认可,派生类中的构造,析构可以调用到基类的构造析构是由编译器编译中实现的.即:在子类构造函数开头自动添加默认的基类构造函数或初始化列表中指定的基类构造函数调用;在子类析构函数末尾自动添加其基类析构函数调用.

    至于为什么会先调用基类构造函数再调用子类构造函数,先调用子类析构函数再调用基类析构函数.我认为:因为只可能出现子类中成员依赖基类成员的存在而存在,而不会出现基类中成员依赖子类成员存在.例如:子类中有一个成员是基类中一个指针成员所指向对象的引用.则这种情况下倘若没有先调用基类构造函数对其指针成员初始化创建对象.那子类引用初始化时便不知会指向何处.同样析构时倘若先调用基类将其中的对象释放后,此时子类中引用变量在做一下善后处理时也便没有任何意义,因而其指向对象已经释放掉了. 派生类对象中基类成员先于子类成员存在,后于子类对象消失.

    不知道初始化列表中倘若基类构造函数在其他子类成员初始化之后生成的代码中基类构造函数调用是否还会在其他代码之前.这样子在GCC中会报警,但可以编译通过,而且感觉其生成代码中也会按照初始化列表中顺序调用.即构造函数调用被放到了其他子类成员后面,因为代码就是这么写的.(这句我也不是那么确定的)



    关于派生类构造函数与基类构造函数的调用顺序问题,我们先看一下书上的说法:
        《面向对象程序设计基础(第二版》李师贤等,第254页:C++语言的基本规则是:创建一个派生类的对象时,如果基类带有构造函数,则先调用基类的构造函数,然后才调用派生类的构造函数。 
        《Thinking in C++》,刘宗田等译,第261页:可以看出,构造在类层次的最根处开始,而在每一层,首先调用基类构造函数,然后调用成员对象构造函数。 
        《C++ Primer Plus(第四版)中文版》,孙建春等译,第399页:记住:创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。 
        真的是这样吗? 
        一个类的对象在实例化时,这个类的构造函数会被调用。如果承认这一点,就会发现上述论断的矛盾之处。一个派生类的对象,在实例化时,不调用作为产生它的类 的构造函数,而先去调用别的类的构造函数,这符合逻辑吗?再考虑一下基数构造函数有参数的的时候,派生类构造函数的定义形式,“派生类构造函数可以使用初 始化列表机制将值传递给基类构造函数”(《C++ Primer Plus(第四版)中文版》第399页)。如果是基类的构造函数先被调用,那么它所使用的参数从何而来? 
        前两本书在说明这一规则时,毫无例外地在派生类构造函数和基类构造函数中使用cout输出一些信息来表明相应的构造函数被调用了,并以此说明构造函数的调 用顺序。在这里,我要指出的是:这一顺序,仅仅是这些cout输出的顺序,并不能说明是函数调用的顺序。真正调用的过程,单纯依赖于C++是看不到的。 
        我们可以用这样的实验来证明这一点。选择前两本书关于这一规则的任何一个实例,在Visual Studio中,分别对派生类和基类的构造函数下断点,注意:断点要下在函数定义函数名处,这样才是真正函数执行的起点,而不能下在cout语句上,那是 函数体,不能说明问题。然后调试这个程序,你会发现派生类构造函数的断点先中断,基类的构造函数断点后中断。如果你有汇编的知识,那么请打开汇编语言的开 关,这之间的关系就更明显了。 
        现在可以更确切地说明这个规则了:派生类对象在实例化时,派生类构造函数先被执行,在执行过程中(在实例化派生类成员前),调用基类构造函数,然后(在基类成员实例化后)返回派生类构造函数实例化派生类成员。 
        析构函数的顺序问题依此类推。


    一、缺省构造函数的调用关系

    #include <iostream> 
    #include <string> 
    using namespace std;  
    
    class CBase {
     string name;     
    int age; 
    public:     
    CBase() { cout << "BASE" << endl;  }     
    ~CBase() {         cout << "~BASE" << endl;     } 
    };  
    
    class CDerive : public CBase { 
    public:     
    CDerive() {cout << "DERIVE" << endl;     }     
    ~CDerive() {          cout << "~DERIVE" << endl;     } 
    }; 
    
     int main ( )  {     
    CDerive d;      
    return 0; 
    }
    
    BASE
    DERIVE
    ~DERIVE
    ~BASE
    (int)0 二、有参数时的传递 #include <iostream> #include <string> using namespace std; class CBase {  string name; public: CBase(string s) : name(s) { cout << "BASE: " << name << endl; } ~CBase() { cout << "~BASE" << endl; } };   class CDerive : public CBase {  int age; public: CDerive(string s, int a) : CBase(s), age(a) { cout << "DERIVE: " << age << endl; } ~CDerive() { cout << "~DERIVE" << endl; } }; int main ( ) { CDerive d("John", 27); return 0; } BASE: John
    DERIVE: 27
    ~DERIVE
    ~BASE
    (int)0 三、祖孙三代的参数传递 #include <iostream> #include <string> using namespace std; class CBase { string name; public: CBase(string s) : name(s) {cout << "BASE: " << name << endl; } ~CBase() { cout << "~BASE" << endl; } }; class CDerive : public CBase {  int age;  public: CDerive(string s, int a) : CBase(s), age(a) { cout << "DERIVE: " << age << endl; } ~CDerive() { cout << "~DERIVE" << endl; } }; class CSon : public CDerive { string id; public: CSon(string s1, int a, string s2) : CDerive(s1, a), id(s2) {cout << "SON: " << id << endl; } ~CSon() { cout << "~SON" << endl; } }; int main ( ) { CSon s("John", 27, "8503026"); return 0; } BASE: Jhon
    DERIVE: 27
    SON: 8503026
    ~SON
    ~DERIVE
    ~BASE
    (int)0 四、多重继承的参数传递 多重继承时参数的传递方法和上面一样,要注意的是两个基类的顺序。决定2个基类的顺序是知27行。 将27行的CBase1和CBase2的顺序交换一下,其结果中BASE1和BASE2的顺序也随之改变,与第30行无关。 #include <iostream> #include <string> using namespace std; class CBase1 { string name; public: CBase1(string s) : name(s) { cout << "BASE1: " << name << endl; } ~CBase1() { cout << "~BASE1" << endl; } }; class CBase2 { int age;  public: CBase2(int a) : age(a) { cout << "BASE2: " << age << endl; } ~CBase2() { cout << "~BASE2" << endl; } }; class CDerive : public CBase1, public CBase2 { string id; public: CDerive(string s1, int a, string s2) : CBase1(s1), CBase2(a), id(s2) { cout << "DERIVE: " << id << endl; } ~CDerive() { cout << "~DERIVE" << endl; }  }; int main ( ) { CDerive d("John", 27, "8503026"); return 0; } BASE1: John
    BASE2: 27
    DERIVE: 8503026
    ~DERIVE
    ~BASE2
    ~BASE1
    (int)0

    展开全文
  • 析构函数
  • 我知道,基类的析构函数要生命为虚函数,不然用基类指针删除派生对象时只会调用基类的析构函数,而不会调用派生析构函数,从而发生内存泄露。但是为什么会这样?为什么基类析构函数不是虚函数时,就不会调用...
  • C++关于析构函数无法调用的问题 代码如下: #include <iostream> using namespace std; class A {//创建一个A public: A(int a) { cout << "A()..." << endl; m_a = a; } void ...
  • 派生的构造函数与析构函数调用顺序 前面已经提到,构造函数和析构函数的调用顺序是先构造的后析构,后构造的先析构。 那么基类和派生类中的构造函数和析构函数的调用顺序是否也是如此呢? 构造函数的调用...
  • Python析构函数的调用 如果不删除,则不会调用析构函数。 当删除时,析构函数被调用。
  • 当派生的对象从内存撤销时一般先调用派生析构函数,然后再调用基类的析构函数。1、系统会只执行基类的析构函数,而不执行派生析构函数。如下例子 [例12.3] 基类有非虚析构函数时的执行情况。为简化...
  • (1) 调用派生自身的析构函数; (2) 调用派生成员对象的析构函数; (3) 调用基类的析构函数。 例: 一个派生对象结束其生命周期时( )。 A先调用派生析构函数,后调用基类的析构函数 B先...
  • 4、不能显式地调用析构函数,而是由系统自动调用。 定义析构函数格式为: ~类名() {…}  由于c#得到自动内存管理的支持,当对象生命期结束后,系统能够自动回收那些对象应该释放的资源等,所以一般不需要程序员...
  • 的析构函数调用方式堆和栈结论系统什么情况下不会自动调用析构函数呢?举例参考 堆和栈 为了理解这个问题,我们必须首先弄明白“堆”和“栈”的概念。 堆区(heap) —— 一般由程序员分配释放, 若程序员不...
  • 如果由析构函数在main函数返回后调用,内存泄漏检测函数应该放在这个析构函数的末尾。 举例说明如下
  • 而当调用函数有一个接受对象时,就将返回对象赋值给接收对象,这个返回对象调用函数结束时调用析构函数。3. 当有一个带有一个参数的构造函数时,可以用这个参数同类型的数据初始化这个对象,默认会调用这个...
  • 构造/析构函数中不要调用虚函数

    千次阅读 2013-10-21 22:11:44
    那么在调用的时候,如果析构函数中对于虚函数还执行虚机制,就有可能已经执行过一个子对象的析构函数,又去调用子对象的函数,这样会很危险。所以析构函数中,对于虚函数,只会执行目前最外一级的那个函数。  ...
  • 简要结论: 1. 从语法上讲,调用完全没有问题。... 同样,进入基类析构函数时,对象也是基类类型。 所以,虚函数始终仅仅调用基类的虚函数(如果是基类调用虚函数),不能达到多态的效果,所以放在构造函数...
  • C++,提倡不能构造函数和...那为何提倡不能构造函数与析构函数中不能调用虚函数。接下来我们通过代码分析构造函数或者虚构函数中调用虚函数是否可行。假设我们有两种商品A, B。 我们要记录着两种商品的销售记
  • 如果一个基类的析构函数被说明为虚析构函数,则它的派生类中析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明。 说明虚析构函数的目的在于使用delete运算符删除一个对象时,能保析构
  • C++类析构函数

    千次阅读 2019-03-11 20:57:17
    析构函数是C++释放类对象的函数,如果在类中不写析构函数,C++编译器会自动...析构函数在对象释放的时候自动调用,在delete 删除对象的时候,也会调用析构函数。 class Demo { public: Demo() { //无参数构造...
  • 基类和派生的构造函数和析构函数中调用函数,会不会体现出多态性呢?Jungle做了以下测试: 首先定义了基类Base和虚函数fun,并构造函数和析构函数中调用了fun(); 然后定义了派生Derived1继承了Base,并...
  • 对 c++成员函数的构造函数 拷贝构造函数 析构函数 赋值运算符重载的一些特点以及函数调用顺序,并附上例子,希望对大家有帮助,有错误大家可以指出来。
  • 主要讲解构造函数与析构函数调用顺序,比较适合新人
  • 析构函数调用

    2020-10-07 21:22:41
    析构函数调用
  • 详细描述:如下所示”代码块1“,每次在调用encrypt()函数时均会申请一次内存,如何在析构函数中一次性销毁所有产生的内存,”代码块2“的方法不符合要求 扩展:如果上述问题无较好答案,是否有其他方法可以避免...
  • 继承关系构造函数、析构函数调用顺序

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 144,350
精华内容 57,740
关键字:

在类其他函数中调用类的析构函数