zoukankan      html  css  js  c++  java
  • 再探析构函数

    再探析构函数

    调用析构函数时机

    无论何时一个对象被销毁,就会自动调用其析构函数:

    • 变量在离开其作用域时被销毁
    • 当一个对象被销毁时,其成员被销毁
    • 容器(无论是标准库容器还是数组)被销毁时,其元素被销毁。
    • 对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁
    • 对于临时对象,当创建它的完整表达式结束时被销毁

    使用=default

    C++11新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上=default来要求编译器生成构造函数。

    class Sales_data{
    public:
    	Sales_data() = default;
        Sales_data(const Sales_data&) = default;
        Sales_data& operator = (const Sales_data &);
        ~Sales_data() = default;
    };
    
    Sales_data& Sales_data::operator=(const Sales_data&) = default;
    

    (ps:感觉没啥用。。)

    使用=delete

    大多类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符,无论是隐式地还是显式地,但对于某些类来说,这些操作没有合理的意义。在此情况下,定义类时必须采用某种机制阻止拷贝或赋值。

    例如,iostream类阻止了拷贝,以避免多个对象写入或读取相同的IO缓冲。

    使用=delete定义删除的函数

    struct NoCopy{
        NoCopy() = default; //使用合成的默认构造函数
        NoCopy(const NoCopy&) = delete; //阻止拷贝
        NoCopy &operator=(const NoCopy&) = delete; //阻止赋值
        ~NoCopy() = default;
    };
    

    析构函数不能是删除的成员

    对于一个删除了析构函数的类型,编译器将不允许定义该类型的变量或创建该的临时对象。(这里同理Singleton单例模式中的将析构函数私有化,如果将析构函数私有化后,将不允许在栈上创建类对象,但可以动态分配这种类型的对象)虽然不能定义这种类型的变量或成员,但可以动态分配这种类型的对象。但是不能释放这些对象。

    #include <iostream>
    
    using namespace std;
    
    struct NoDtor{
        NoDtor() = default;
        ~NoDtor() = delete;
    };
    
    int main()
    {
        //NoDtor nd; //erorr,NoDtor的析构函数delete
        NoDtor *p = new NoDtor(); 
        //delete p; //error,NoDtor的析构函数delete
        return 0;
    }
    

    合成的拷贝控制成员可能是删除的

    如果一个类的数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除的。同时如上所说,如果一个成员有删除的或不可访问的析构函数会导致合成的默认和拷贝构造函数被定义为删除的,其原因是如果没有这条规则,我们可能会创建出无法销毁的对象。

    虚析构

    当我们用父类的指针来操作一个子类,在释放的时候,因为delete的父类指针,会造成子类申请的空间未释放,因此也需要用虚函数的原理应用于析构函数上。

    继承机制中,析构函数的调用顺序和构造函数相反,先释放子类再释放基类。

    #include <iostream>
    using std::cout;
    using std::endl;
    
    class Base
    {
    public:
    	Base(double base)
    		: _base(new double(base))
    	{
    		cout << "Base(double)" << endl;
    	}
    
    	virtual
    		void print() const
    	{
    		cout << "*_base = " << *_base << endl;
    	}
    
    	//一般情况下,只要基类中有一个虚函数,都要将析构函数设为虚函数
    	virtual
    		~Base()   //析构函数只有一个
    	{
    		if (_base) {
    			delete _base;
    			cout << "~Base() " << endl;
    		}
    	}
    
    private:
    	double * _base;
    };
    
    class Derived
    	: public Base
    {
    public:
    	Derived(double base, double derived)
    		: Base(base)
    		, _derived(new double(derived))
    	{
    		cout << "Derived(double, double)" << endl;
    	}
    
    	//virtual 
    	void print() const
    	{
    		Base::print();//直接去方法区拿
    		cout << "*_derived = " << *_derived << endl;
    	}
    
    	//virtual
    	~Derived()
    	{
    		if (_derived) {
    			delete _derived;
    			cout << "~Derived() " << endl;
    		}
    	}
    private:
    	double * _derived;
    };
    
    int main(void)
    {
    	Base * pbase = new Derived(11.11, 12.12);
    	pbase->print();
    
    	delete pbase;
    	return 0;
    }
    

    纯虚析构

    和纯虚函数不同,因为在释放内存时基类实际上也释放了内存,所以在纯虚析构中必须类内声明,类外实现。

    class Animal{
    	virtual ~Animal() = 0;
    };
    
    Animal::~Animal()
    {
        cout << "~Animal()" << endl;
    }
    

    同样,含纯虚析构函数的类也是抽象类,不能进行实例化对象!


    参考:《C++ Primer》第五版

  • 相关阅读:
    Git安装及密钥的生成并上传本地文件到GitHub上
    进阶攻略|前端最全的框架总结
    进阶攻略|前端最全的框架总结
    干货|几个有用的问答平台
    干货|几个有用的问答平台
    美食篇之御桥小聚
    美食篇之御桥小聚
    Java EE (8) -- Java EE Patterns
    css---position、z-index、opacity、overflow、hover、background
    T1082 线段树练习3 codevs
  • 原文地址:https://www.cnblogs.com/Mered1th/p/10920500.html
Copyright © 2011-2022 走看看