C++超详细讲解析构函数

目录
  • 特性
  • 析构函数处理自定义类型
  • 编译器生成的默认析构函数

特性

析构函数是特殊的成员函数

特征如下:

  • 析构函数名是~类名;
  • 无参数无返回值;
  • 一个类有且只有一个析构函数;
  • 对象声明周期结束,编译器自动调用析构函数;
class Stack
{
public:
	Stack(int capacity = 4)
		:
		_size(0),
		_capacity(capacity),
		_p(new int[_capacity])
	{
		cout << "Stack(int capacity = 4)" << endl;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		if (_p)
		{
			delete[](_p);
            _p = nullptr;
		}
		_size = _capacity = 0;
	}
private:
	int _capacity;
	int _size;
	int* _p;
};
int main()
{
	Stack s;
	return 0;//程序结束,调用s的析构函数
}

输出:

析构函数处理自定义类型

class String
{
public:
	String(const char* str = "songxin")
	{
		cout << "String(const char* str = \"songxin\")" << endl;
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
		_str = nullptr;
	}
private:
	char* _str;
};
class Person
{
public:
	Person()
		:
		_age(20),
		_name()
	{
		cout << "Person()" << endl;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
private:
	String _name;
	int _age;
};
int main()
{
	Person p;
	return 0;
}

输出:

析构函数在程序即将结束时,调用了Person的析构函数,在Person类的析构函数即将结束接着调用String类的析构函数。

归纳一下:

析构函数是与构造函数执行相反的操作的,构造函数负责给对象成员变量初始化并加载资源,而析构函数则是给对象的成员变量清理资源,而不是清理对象本身。

编译器生成的默认析构函数

编译器默认生成的析构函数能做些什么工作呢?我们前面已经介绍了编译器生成的构造函数会去只会处理自定义类型的成员变量,那么析构既然和构造相对应,析构也应该是只去处理自定义类型的成员变量吧,确实如此,析构函数不会对内置类型有任何处理,只会在调用自身的析构后再去调用自定义类型成员的析构。

关于编译器自动生成的析构函数,下面的程序我们会看到,编译器生成的析构函数,会对自定类型成员调用它的析构函数。

class String
{
public:
	String(const char* str = "songxin")
	{
		cout << "String(const char* str = \"songxin\")" << endl;
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
		_str = nullptr;
	}
private:
	char* _str;
};
class Person
{
public:
	Person()
		:
		_age(20),
		_name()
	{
		cout << "Person()" << endl;
	}

private:
	String _name;
	int _age;
};
int main()
{
	Person p;
	return 0;
}

输出:

默认生成的析构函数对成员变量的处理

  • 内置类型不处理;
  • 自定义类型成员调用相应的析构函数;

那成员变量中的内置类型处不处理其实都无所谓嘛,反正都要归还给操作系统,但是有例外:

如果成员变量含有指针,并且指针指向一块我们正使用的空间,指针也是内置类型,那如果不释放指针指向的那块空间就会造成内存泄漏,而编译器生成的析构函数是不会处理此情况的,因为需要我们在析构函数中主动释放内存,也就是说需要我们显式的去定义析构函数。

class Stack
{
public:
	Stack(int capacity = 4)
		:
		_size(0),
		_capacity(capacity),
		_p(new int[_capacity])//使用new去申请内存
	{
		cout << "Stack(int capacity = 4)" << endl;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		if (_p)
		{
			delete[](_p);//释放内存
            _p = nullptr;
		}
		_size = _capacity = 0;
	}
private:
	int _capacity;
	int _size;
	int* _p;
};
int main()
{
	Stack s;
	return 0;//程序结束,调用s的析构函数
}

析构函数无论是我们显式定义的还是编译器生成的,都会在对象的声明周期结束时自动调用,并且会调用自定义类型成员变量的析构函数来释放资源,而对内置类型不做处理。

可以不显式定义析构函数的情况

  • 类的成员都是自定义类型的;
  • 类的成员都是非指针的内置类型;
  • 成员有指针,但并没有管理内存资源;

如果类的成员变量有指针类型,并且我们让指针指向了一块动态分配的空间,那么就需要我们自己写析构函数了。

总结:不是类直接管理另一块内存资源的,就不需要写析构函数,编译器自己生成的就能处理。

到此这篇关于C++超详细讲解析构函数的文章就介绍到这了,更多相关C++析构函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++分析类的对象作类成员调用构造与析构函数及静态成员

    目录 类对象作为成员 静态成员 定义和分类 静态成员变量 静态成员函数 总结 类对象作为成员 C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员 例如: class Phone {} class Person { Phone p: } tips:当类中成员是其他类对象时,我们称该成员为 对象成员 Person类中有对象p作为成员,Phone为对象成员,那么当创建Person对象时,Phone与Person的构造和析构的顺序是谁先谁后? 那让我们在两个类中加上一些输出语句做提示就好了,

  • C++ virtual destructor虚拟析构函数

    概述 虚析构函数 (virtual destructor) 可以帮我们实现基类指针删除派生类对象. 问题 当我们从派生类的对象从内存中撤销时会先调用派生的析构函数, 然后再基类的析构函数, 由此就会产生问题: 如果用 new 运算符建立了派生类对象, 并且由一个基类的指针比那里指向该对象 用 delete 运算符撤销对象时, 系统只执行基类的析构函数. 而不执行派生类的析构函数, 派生类对象析构中要求的工作将被忽略 Base 类: #ifndef PROJECT6_BASE_H #define

  • C++踩坑实战之构造和析构函数

    目录 前言 构造函数 通过构造函数实现的类型转换 派生类的构造函数 析构函数 继承中的析构函数 应用 总结 前言 我是练习时长一年的 C++ 个人练习生,喜欢野指针.模板报错和未定义行为(undefined behavior).之前在写设计模式的『工厂模式』时,一脚踩到了构造.继承和 new 组合起来的坑,现在也有时间来整理一下了. 构造函数 众所周知:在创建对象时,防止有些成员没有被初始化导致不必要的错误,在创建对象的时候自动调用构造函数(无声明类型),完成成员的初始化.即: Class c

  • C++深入讲解对象的销毁之析构函数

    目录 一.对象的销毁 二.析构函数 三.小结 一.对象的销毁 生活中的对象都是被初始化后才上市的 生活中的对象被销毁前会做一些清理工作 —股而言,需要销毁的对象都应该做清理 解决方案 为每个类都提供一个 public 的 free 函数 对象不再需要时立即调用 free 函数进行清理 如下: 存在的问题 free 只是一个普通的函数,必须显示的调用 对象销毁前没有做清理,很可能造成资源泄漏 C++ 编译器是否能够自动调用某个特殊的函数进行对象的清理? 二.析构函数 C++ 的类中可以定义一个特殊

  • 详解C++中的析构函数

    简介 析构函数(Destructors),是对象的成员函数,没有返回值也没有参数,且一个类只有一个析构函数,当对象被销毁的时候调用,被销毁通常有这么几个情况. 函数执行结束 程序执行结束 程序块包含的局部变量 delete操作 什么时候要自己写析构函数? 编译器会自动创建默认的析构函数,通常都没有问题,但是当我们在类中动态分配了内存空间时,我们需要手段的回收这块空间,防止内存溢出.就像这样 class String { private: char *s; int size; public: St

  • C++超详细讲解构造函数与析构函数的用法及实现

    目录 写在前面 构造函数和析构函数 语法 作用 代码实现 两大分类方式 三种调用方式 括号法 显示法 隐式转换法 正确调用拷贝构造函数 正常调用 值传递的方式给函数参数传值 值传递方式返回局部对象 构造函数的调用规则 总结 写在前面 上一节解决了类与对象封装的问题,这一节就是对象的初始化和清理的构造函数与析构函数的内容了:对象的初始化和清理也是两个非常重要的安全问题:一个对象或者变量没有初始状态,对其使用后果是未知,同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题:c++利用了构

  • C++中构造函数与析构函数的详解及其作用介绍

    目录 构造函数 默认构造函数 有参构造函数 析构函数 析构函数例子 析构函数执行时机 局部对象 全局对象 构造函数 构造函数 (constructor) 是一种特殊的成员函数. 它会在每次创建类的新对象时执行. 构造函数的名称与类的名称是完全相同的, 并且不会返回任何类型. 构造函数可用于为某些成员变量设置初始值. 格式: Class::Class(); // 构造函数 默认构造函数 如果用户自己没有定义构造函数, C++ 系统会自动生成一个默认构造函数. 这个构造函数体是空的, 没有参数, 不

  • C++编程析构函数拷贝构造函数使用示例详解

    目录 构造函数 析构函数 拷贝构造之深拷贝和浅拷贝 深浅拷贝区别 首先定义一个类进行操作. class MM { public: protected: int year; string name; } 构造函数在类中默认有一个无参的构造函数 默认的构造函数为 类名(){}:这个构造函数 如果直接写了构造函数那么这个构造函数将会没有 构造函数 class MM { public: //MM() {};//无参构造函数 MM(int year, string name) :year(year), n

  • 正确理解C++的构造函数和析构函数

    目录 一.构造函数 二.C++类的内存模型 2.1.只定义成员函数 2.2.往空类中添加静态成员变量 2.3.再加入非静态成员变量 三.this指针 四.析构函数 一.构造函数 首先,由于类只是一个模板,因此我们在定义类时无法对成员变量初始化,比如下面代码就是错误的: class circle{ public: int m_L = 20; // Error:不允许使用数据成员初始值设定项 }; 因此,初始化只能发生在类创建对象的过程中,但是由于访问权限的原因,无法在类外访问某些成员变量,因此下面

  • C++超详细讲解析构函数

    目录 特性 析构函数处理自定义类型 编译器生成的默认析构函数 特性 析构函数是特殊的成员函数 特征如下: 析构函数名是~类名: 无参数无返回值: 一个类有且只有一个析构函数: 对象声明周期结束,编译器自动调用析构函数: class Stack { public: Stack(int capacity = 4) : _size(0), _capacity(capacity), _p(new int[_capacity]) { cout << "Stack(int capacity =

  • C++超详细讲解拷贝构造函数

    目录 构造函数 特征 编译器生成的拷贝构造 拷贝构造的初始化列表 显式定义拷贝构造的误区 结论 构造函数 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用 拷贝构造函数是构造函数的一个重载,因此显式的定义了拷贝构造,那么编译器也不再默认生成构造函数. 特征 拷贝构造也是一个特殊的成员函数 特征如下: 拷贝构造是构造函数的一个重载: 拷贝构造的参数只有一个并且类型必须是该类的引用,而不是使用传值调用,否则会无限递归: 若没有显

  • C++超详细讲解模板的使用

    目录 一.函数模板 1.1函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 二.类模板 2.1 类模板的定义格式 2.2类模板的实例化 总结 一.函数模板 1.1函数模板概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本. 1.2 函数模板格式 template<typename T1, typename T2,…,typename Tn> 返回值类型 函数名(参数列表){} template<

  • C++超详细讲解泛型

    目录 1.了解泛型编程 2.函数模板 2.1简单示例 2.2多个模板参数 2.3模板实例化 2.4模板和普通函数同时存在 2.5函数模板不支持定义和声明分离 3.类模板 3.1简单示例 3.2成员函数声明和定义分离 1.了解泛型编程 就好比活字印刷术,可以灵活调整印刷的板块和内容,比只能固定印刷某一个内容的雕版印刷术效率更高,也让印刷术由此得到了更广泛的应用. 在C++中,函数重载和模板的出现,让泛型编程得到了实际的应用.其中模板,就是类似活字印刷术一样的存在. 2.函数模板 八八了那么多没用的

  • C++超详细讲解模拟实现vector

    目录 1. 模拟实现vector 2. vector常用接口 2.1 reserve 2.2 resize 2.3 push_back 2.4 pop_back() 2.5 insert 2.6 erase 2.7 构造函数的匹配问题 3. 更深层次的深浅拷贝问题 1. 模拟实现vector 我们模拟实现是为了加深对这个容器的理解,不是为了造更好的轮子. 快速搭一个vector的架子 // vector.h #pragma once #include <assert.h> // 模拟实现 --

  • C++ Boost Variant示例超详细讲解

    目录 一.提要 二.示例 一.提要 Boost.Variant 提供了一个类似于 union 的名为 boost::variant 的类.您可以将不同类型的值存储在 boost::variant 变量中.在任何时候只能存储一个值.分配新值时,旧值将被覆盖.但是,新值的类型可能与旧值不同.唯一的要求是这些类型必须作为模板参数传递给 boost::variant,这样它们才能为 boost::variant 变量所知. boost::variant 支持任何类型.例如,可以将 std::string

  • java反射超详细讲解

    目录 Java反射超详解✌ 1.反射基础 1.1Class类 1.2类加载 2.反射的使用 2.1Class对象的获取 2.2Constructor类及其用法 2.4Method类及其用法 Java反射超详解✌ 1.反射基础 Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法:对于任意一个对象,都能够知道它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制. Java反射机制主要提供以下这几个功能: 在运行时判断任意一个对象所属

  • 超详细讲解Linux C++多线程同步的方式

    目录 一.互斥锁 1.互斥锁的初始化 2.互斥锁的相关属性及分类 3,测试加锁函数 二.条件变量 1.条件变量的相关函数 1)初始化的销毁读写锁 2)以写的方式获取锁,以读的方式获取锁,释放读写锁 四.信号量 1)信号量初始化 2)信号量值的加减 3)对信号量进行清理 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通过多线程模拟多窗口售票为例: #include <iostream> #include<pthread.h> #include<stdio.h&

  • 超详细讲解Linux DHCP服务

    目录 一.DHCP服务(动态主机配置协议) 1.背景 2.概述 3.优点 4.DHCP报文类型 5.DHCP 的分配方式 二.安装 DHCP 服务器 1.DHCP 服务软件 2.主配置文件 三.配置步骤 1.使用 DHCP 动态的给 PC 机分配 IP 地址 ① eNSP ②虚拟机 ③验证 ④进入命令行"ipconfig"测试 一.DHCP服务(动态主机配置协议) 1.背景 1.手动设置工作量大且容易冲突 2.用DHCP可以减少工作量和避免地址冲突 2.概述 作用:为局域网内的电脑分配

随机推荐