精华内容
下载资源
问答
  • 最近做一个项目,在调试bug时,总结出了一个C++的析构函数调用的时机,写此博客,免得以后再犯此类错误。 类的定义:#include class CTest1 { public: CTest1(); ~CTest1(); int Func1(); };类的实现:#...

    最近做一个项目,在调试bug时,总结出了一个C++的析构函数被调用的时机,写此博客,免得以后再犯此类错误。
    类的定义:

    #include <iostream>
    class CTest1
    {
    public:
        CTest1();
        ~CTest1();
    
    
        int Func1();
    };

    类的实现:

    #include "Test1.h"
    
    CTest1::CTest1()
    {
        std::cout << "CTest1()" << std::endl;
    }
    
    
    CTest1::~CTest1()
    {
        std::cout << "~CTest1()" << std::endl;
    }
    
    int CTest1::Func1()
    {
        std::cout << "Func1()" << std::endl;
        return 1;
    }

    主函数文件:

    int Func(CTest1 ct)
    {
        int iret = ct.Func1();
        return iret;
    }
    int main()
    {
        CTest1 ct;
        for (int i = 0; i < 3; ++i)
        {
            Func(ct);
        }
        getchar();
        return 0;
    }

    执行结果:

    CTest1()
    Func1()
    ~CTest1()
    Func1()
    ~CTest1()
    Func1()
    ~CTest1()

    如果这样定义int Func(CTest1* ct),则执行结果才是预期。
    总结一下。实例为非类的指针,析构在离开函数后,会自动执行,即使定义来自函数参数。
    项目中为了防止此类错误,能使用delete控制执行析构函数的,最好使用delete.

    展开全文
  • 析构函数何时调用

    2019-10-03 22:19:32
    总结了下,析构函数调用情况分以下几类: 1.对象生命周期结束,自动销毁;(如对象作为参数进行函数值传递) 2.delete指向对象的指针,或者delete指向基类的指针,且基类的析构函数是虚函数; 3.当类中包含其他...

    总结了下,析构函数的调用情况分以下几类:

    1.对象生命周期结束,自动销毁;(如对象作为参数进行函数值传递)

    2.delete指向对象的指针,或者delete指向基类的指针,且基类的析构函数是虚函数;

    3.当类中包含其他类成员,当该类的析构函数调用时,成员对应的类的析构函数也会被调用。

    转载于:https://www.cnblogs.com/tylerhuang/p/4928989.html

    展开全文
  • 析构函数调用顺序

    2020-03-24 10:33:33
    如果是多个对象,调用构造函数的次序与调用析构函数的次序相反 对于不同作用域和存储类别的对象,构造函数he析构函数调用顺序 全局对象 构造函数在文件中所有函数执行前调用 当main函数执行完毕或者是调用exit...

    如果是多个对象,调用构造函数的次序与调用析构函数的次序相反
    对于不同作用域和存储类别的对象,构造函数he析构函数的调用顺序
    全局对象

    • 构造函数在文件中所有函数执行前调用

    • 当main函数执行完毕或者是调用exit函数时(此时程序会终止),调用析构函数。
      函数中定义的自动局部对象(例如在函数中定义对象)

    • 在建立对象时调用其构造函数,如果函数被多次调用,则在每次建立对象时都要调用构造函数

    • 在函数调用结束、对象释放时先调用析构函数
      函数中定义的静态局部对象

    • 第一次调用次函数建立对象时调用构造函数一次

    • 在调用结束时对象并不会被释放,因此也不会调用析构函数,只在main函数结束或者调用exit函数结束程序时,才调用析构函数
      下面是一个关于析构函数调用顺序的举例
      关于全局对象、静态局部对象、主函数的对象的析构函数调用顺序:

    //不同作用域和存储类别的对象构造函数和析构函数的调用顺序
    //在写这个代码时遇到了一个问题,几个警告:
    /*  我使用的编译器是Visual Studio 2019
    出现了一个错误:	E0167	"const char *" 类型的实参与 "char *" 类型的形参不兼容
    最后网上搜到了CSDN的以为大神的博客,在项目属性->C/C++->语言中的符合模式项选择否
    然后解决掉了
    还有一个错误,错误代码是C4996,编译器警告登记为3级,打开查看了一下详情,貌似是由于strcpy不太安全,有更安全的strcpy_s
    于是我搜索了一下strcpy_s的使用方法,看了一位大神的博客,我的理解是将strcpy换成strcpy_s即可,但是使用的时候不正确,
    头文件<cstring>和<string.h>都试了
    最后选择了禁用弃用的解决方法:项目属性->C/C++->高级->禁用特定警告编辑:4996解决掉
    */
    #include<iostream>
    #include<string.h>
    using namespace std;
    class Test
    {
    private:
    	int a;
    	char* str;
    public:
    	Test(int b, char* s)//构造函数
    	{
    		a = b;
    		str = new char[strlen(s) + 1];
    		strcpy(str, s);
    		cout << str << " 构造函数 "<<endl;
    	}
    	void setA(int b)
    	{
    		a = b;
    	}
    	void setStr( char* s)
    	{
    		str = new char[strlen(s) + 1];
    		//需要新建里一个存储空间,否则如果数据过长的话会出错
    		strcpy(str, s);
    	}
    	int getA()
    	{
    		return a;
    	}
    	void show()
    	{
    		cout << a << " , " << str << endl;
    	}
    	Test(const Test& C)//复制构造函数
    	{
    		a = C.a;
    		str = new char[strlen(C.str) + 1];
    		strcpy(str, C.str);
    	}
    	~Test()
    	{
    		cout << str << " 析构函数 " << endl;
    		delete str;
    	}
    };
    Test demo1(100, "hello");//定义一个全局对象,并且使用构造函数初始化
    void external_function(char xx[])//外部函数void f(char *xx)也可以
    {
    	cout << "external_function : " << endl;
    	Test Local_objects(90, xx);//函数中定义的自动局部对象
    	static Test static_demo(0, "static");//函数中定义的静态(static)局部对象
    	Local_objects.show();
    	static_demo.show();
    	static_demo.setA(static_demo.getA() + 10);
    }
    int main()
    {
    	Test demo2(demo1);//定义demo2并用demo1初始化demo2
    	demo1.show();//100,hello
    	demo2.show();
    	demo2.setA(80);//b.a=80;
    	demo2.setStr("abc");//b.str=abc;
    	demo1.show();
    	demo2.show();
    	external_function("111");
    	external_function("222");
    	external_function("333");
    	return 0;
    }
    

    输出的结果:

    /*
    输出结果:
    
    hello 构造函数
    100 , hello
    100 , hello
    100 , hello
    80 , abc
    external_function :
    111 构造函数
    static 构造函数
    90 , 111
    0 , static
    111 析构函数
    external_function :
    222 构造函数
    90 , 222
    10 , static
    222 析构函数
    external_function :
    333 构造函数
    90 , 333
    20 , static
    333 析构函数
    abc 析构函数       主函数的对象的析构函数
    static 析构函数    静态对象的析构函数
    hello 析构函数     全局对象的析构函数
    */
    

    关于不同作用域和存储类别的对象,构造函数和析构函数的调用顺序
    主函数内的对象的析构函数是先被调用的,其次是静态局部对象,最后是全局对象
    关于strcpy_s参考了:strcpy_s函数
    关于解决E0167,参考了:形参不兼容错误类型

    展开全文
  • 1、构造函数的调用顺序 基类构造函数、对象成员构造函数、...对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数); 2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其

    1、构造函数的调用顺序
    基类构造函数、对象成员构造函数、派生类本身的构造函数
    2、析构函数的调用顺序
    派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序相反
    析构函数在下边3种情况时被调用:
    1.对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数);
    2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
    3.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

    #include "iostream"
    using namespace std;
    class Base
    {
    public:
        Base(){ std::cout<<"Base::Base()"<<std::endl; }
        ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
    };
    
    class Base1:public Base
    {
    public:
        Base1(){ std::cout<<"Base1::Base1()"<<std::endl; }
        ~Base1(){ std::cout<<"Base1::~Base1()"<<std::endl; }
    };
    
    class Derive
    {
    public:
        Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
        ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
    };
    
    class Derive1:public Base1
    {
    private:
        Derive m_derive;//声明一个Derive类的对象m_derive
    public:
        Derive1(){ std::cout<<"Derive1::Derive1()"<<std::endl; }
        ~Derive1(){ std::cout<<"Derive1::~Derive1()"<<std::endl; }
    };
    
    int main()
    {
        Derive1 derive;
        return 0;
    }
    

    这里写图片描述
    构造函数的调用顺序是;首先,如果存在基类,那么先调用基类的构造函数,如果基类的构造函数中仍然存在基类,那么程序会继续进行向上查找,直到找到它最早的基类进行初始化;如上例中类Derive1,继承于类Base与Base1;其次,如果所调用的类中定义的时候存在着对象被声明,那么在基类的构造函数调用完成以后,再调用对象的构造函数,如上例中在类Derive1中声明的对象Derive m_derive;最后,将调用派生类的构造函数,如上例最后调用的是Derive1类的构造函数。

    包含对象成员的类的构造与析构顺序

    class A  
    {  
    public:  
        A()  
        {  
            cout << "A's constructor." << endl;  
        }  
        ~A()  
        {  
            cout << "A's destructor." << endl;  
        }  
    };  
    
    class B  
    {  
    public:  
        B()  
        {  
            cout << "B's constructor." << endl;  
        }  
        ~B()  
        {  
            cout << "B's destructor." << endl;  
        }  
    };  
    
    class C  
    {  
    private:  
        B bInC;  
    public:  
        C()  
        {  
            cout << "C's constructor." << endl;  
        }  
        ~C()  
        {  
            cout << "C's destructor." << endl;  
        }  
        A aInC;  
    };  
    
    class D:public C  
    {  
    public:  
        D()  
        {  
            cout << "D's constructor." << endl;  
        }  
        ~D()  
        {  
            cout << "D's destructor." << endl;  
        }  
        A aInD;  
    private:  
        B bInD;  
    };  
    
    int main(void) {  
        D d;  
        return 0;  
    }  

    输出结果:

    B's constructor.  
    A's constructor.  
    C's constructor.  
    A's constructor.  
    B's constructor.  
    D's constructor.  
    D's destructor.  
    B's destructor.  
    A's destructor.  
    C's destructor.  
    A's destructor.  
    B's destructor.  

    分析如下:
    (1)存在继承关系时,先执行父类的构造函数,再执行子类的构造函数;
    (2)当一个类中含有对象成员时,在启动本类的构造函数之前,先分配对象空间,按对象成员的声明顺序执行他们各自的构造函数,再继续执行本类的构造函数;
    (3)对于非静态的局部对象,他们的析构函数的执行顺序与构造函数相反。
    在本程序中:
    (1)执行main(),需要创建一个对象d,所以,需要执行D的构造函数。而D继承自C,所以先要执行C的构造函数;
    (2)而在C中存在对象成员bInC和aInC,所以,在C的构造函数执行之前,先按声明顺序执行B和A的构造函数,然后执行C的构造函数
    (3)轮到构造d了,但是D中有对象成员aInD和bInD,所以,在D的构造函数执行之前,先按声明顺序执行A和B的构造函数,最后,执行D的构造函数;
    (4)以上所有对象的析构函数以与构造函数的执行顺序相反的顺序执行。

    virtual析构函数
    在C++中,构造函数不能声时为虚函数,这是因为编译器在构造对象时,必须知道确切类型,才能正确的生成对象,因此,不允许使用动态束定;其次,在构造函数执行之前,对象并不存在,无法使用指向此此对象的指针来调用构造函数,然而,析构函数是可以声明为虚函数;C++明白指出,当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义—实际执行时通常发生的是对象的derived成分没被销毁掉。
    看下面的例子:

    class Base
    {
    public:
        Base(){ std::cout << "Base::Base()" << std::endl; }
        ~Base(){ std::cout << "Base::~Base()" << std::endl; }
    };
    
    class Derive :public Base
    {
    public:
        Derive(){ std::cout << "Derive::Derive()" << std::endl; }
        ~Derive(){ std::cout << "Derive::~Derive()" << std::endl; }
    };
    
    int main()
    {
        Base* pBase = new Derive();
        //这种base classed的设计目的是为了用来"通过base class接口处理derived class对象"
        delete pBase;
        return 0;
    }

    运行结果
    从上面的输出结果可以看出,析构函数的调用结果是存在问题的,也就是说析构函数只作了局部销毁工作,这可能形成资源泄漏败坏数据结构等问题;那么解决此问题的方法很简单,给base class一个virtual析构函数;

    class Base
    {
    public:
        Base(){ std::cout<<"Base::Base()"<<std::endl; }
        virtual ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
    };
    
    class Derive:public Base
    {
    public:
        Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
        ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Base* pBase = new Derive();
        delete pBase;
    
        return 0;
    }
    

    运行结果
    由此还可以看出虚函数还是多态的基础,在C++中没有虚函数就无法实现多态特性;因为不声明为虚函数就不能实现“动态联编”,所以也就不能实现多态啦!

    展开全文
  • 构造函数与析构函数及调用顺序构造函数定义特点析构函数定义特点构造函数与析构函数的执行顺序例题 构造函数 定义 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据...
  • 里面的讨论基本上已经给出答案了,派生类的析构函数在执行完后,会自动执行基类的析构函数,这个是编译器强制规定的,没有为什么,甚至你在析构函数调用return都不会立即返回到调用处,而是会先按顺序把析构函数...
  • 参考1:C++继承中构造函数、析构函数调用顺序及虚函数的动态绑定 参考2:构造函数、拷贝构造函数和析构函数的的调用时刻及调用顺序 参考3:C++构造函数与析构函数调用顺序 2.构造函数、析构函数与拷贝构造函数介绍...
  • C++析构函数自动调用问题

    千次阅读 多人点赞 2019-04-17 10:06:09
    当单纯的创建对象的时候,对象存放在栈中,此时在程序块的}后面,系统会自动调用析构函数,释放掉栈空间。 但是,如果创建了指向new来的一块空间的指针的时候,如果在没有显示释放掉new到的堆空间时,系统是不会...
  • 本题是关于析构函数和构造函数的调用: 构造函数用于创建对象, 如果不在类内声明和定义的话, 在创建对象时编译器将自动创建一个空的构造函数, 系统将分配内存空间。析构函数用于在事件结束后对对象的销毁, 不...
  • 老师在课堂上讲到了return语句在执行时会自动调用对象的析构函数。我编写了下述代码测试发现整个程序析构函数调用次数与构造函数不等,这样难道不会产生内存泄漏吗? 源代码如下: #include <iostream>using...
  • C++继承中构造函数、析构函数调用顺序及虚函数的动态绑定 参考2: 构造函数、拷贝构造函数和析构函数的的调用时刻及调用顺序 参考3: C++构造函数与析构函数调用顺序 2.构造函数、析构函数与拷贝构造...
  • 4、不能显式地调用析构函数,而是由系统自动调用。 定义析构函数格式为: ~类名() {…}  由于c#得到自动内存管理的支持,当对象生命期结束后,系统能够自动回收那些对象应该释放的资源等,所以一般不需要程序员...
  • (来自c++primer plus)如果创建的是静态存储类对象,则其析构函数将在程序结束时被自动调用。如果创建的是自动存储类对象,则其析构函数将在程序完成代码块时自动被调用。如果对象是new创建的,则它将在栈内存或...
  • 析构函数一般不显式调用,由系统自动调用,并且遵循先构造的后析构的原则,但是如果显式的由“对象名.析构函数名”系统在执行该语句时只是把它当做类的普通成员函数,在遇到return语句时任会隐式的调用析构函数,...
  • 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:...
  • 就会调用该指针指向的派生类析构函数,而派生类的析构函数自动调用基类的析构函数,这样整个派生类的对象完全被释放。 (2)析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的析构...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,025
精华内容 1,210
关键字:

析构函数自动调用