【C++】类与对象——六个默认成员函数、构造函数的概念和特征,析构函数的概念和特征

这篇具有很好参考价值的文章主要介绍了【C++】类与对象——六个默认成员函数、构造函数的概念和特征,析构函数的概念和特征。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.类的六个默认成员函数

  如果一个类中什么成员都没有,简称为空类。
  空类中真的什么都没有吗?

  并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

class Date {};

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

【C++】类与对象——六个默认成员函数、构造函数的概念和特征,析构函数的概念和特征

2.构造函数

2.1构造函数的概念

  构造函数是C++中的一个特殊函数,用于初始化类对象的数据成员,为对象分配内存并完成一些初始化工作。一个类可以有多个构造函数,但必须满足函数名相同、参数列表不同的条件,称为函数重载
  构造函数有以下特点:

(1)构造函数的函数名与类名相同,并且不需要返回类型的声明,在函数体中也不需要指定 return 语句。

(2)构造函数可以具有参数,用于传递初始值给对象的数据成员

(3)构造函数可以进行重载,支持多个构造函数的存在。

(4)如果一个类没有定义自己的构造函数,编译器会自动生成一个默认的构造函数,该函数不带任何参数并且什么也不做,它会自动初始化类的成员变量并分配内存。

(5) 构造函数可以使用初始化列表进行初始化,这种方式可以提高效率。初始化列表是用冒号:跟在构造函数名后的成员初始化语句,以逗号隔开数据成员的名称和初始值。初始化列表的执行顺序与成员在类中声明的顺序一致。

(6) 如果一个类需要在离开作用域时,自动释放在堆内存上分配的资源,必须定义类的析构函数(Destructor)

总结:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

例子一(Person类):

class Person {
public:
    string name;
    int age;
    Person(string n, int a) { // 带参数的构造函数
        name = n;
        age = a;
    }
};

int main() {
    Person p1("Alice", 20); // 使用构造函数创建对象
    cout << p1.name << ", " << p1.age << endl; // 输出对象的成员变量

    return 0;
}

  我们定义了一个 Person 类,并创建了一个带有参数的构造函数来初始化对象的成员变量。
  在 main() 函数中,我们使用类定义了一个 Person 对象,编译器在创建对象时会自动调用构造函数,初始化对象并输出该对象的成员变量值。

例子二(Date类):

class Date
{
public:
void Init(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

void Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Init(2023, 5, 29);
	d1.Print();
	Date d2;
	d2.Init(2023, 5, 29);
	d2.Print();
	return 0;
}

  这段代码定义了一个名为 Date 的类。Date 类包含了三个私有的数据成员:_year(年份)、_month(月份)和 _day(日期)。该类提供了两个公有的成员函数: Init 和 Print。
  Init 函数用于初始化 Date 对象的年、月、日信息,Print 函数则用于打印出 Date 对象的年、月、日信息。在主函数中,创建了两个 Date 对象,并通过 Init 函数初始化了其年、月、日的信息。

  我们可以使用构造函数来代替Init函数的作用,使创建的数据成员初始化。这两段代码实现的功能完全一样。

#include <iostream>
using namespace std;

class Date
{
public:
    // 默认构造函数
    Date() {
        _year = 1949;
        _month = 10;
        _day = 1;
    }
    // 带参数的构造函数
    Date(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }
    // 输出日期方法
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    // 使用构造函数初始化对象
    Date d1(2023, 5, 29);
    d1.Print();
    Date d2(2023, 5, 29);
    d2.Print();
    return 0;
}

2.2构造函数的特性

  综上所述:构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

  其特征如下:

(1)函数名与类名相同。

(2)无返回值。

(3)对象实例化时编译器自动调用对应的构造函数。

(4)构造函数可以重载。

  在函数 TestDate 中,分别通过无参构造函数和带参构造函数创建了三个 Date 类型的对象(d1、d2 和 d3)。其中,d1 使用了无参构造函数,d2 使用了带参构造函数,并传入年月日参数进行初始化。而 d3 也是通过无参构造函数创建的对象,但使用了错误的语法,即在对象后添加了一对空括号,则编译器会将其解析为函数声明,而不是对象的创建;正确的写法应该是 Date d3;

class Date
{
public:
// 1.无参构造函数
Date()
{}

// 2.带参构造函数
Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

private:
	int _year;
	int _month;
	int _day;
};

void TestDate()
{
	Date d1; // 调用无参构造函数
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
	// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
	// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
	Date d3();
}


(5)如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

  在这段代码中,如果将 Date 类中的构造函数屏蔽掉,那么编译器将会自动生成一个默认的无参构造函数,此时主函数中的代码可以正常编译。但如果取消屏蔽,那么编译器将不再生成默认构造函数,主函数中的代码将会编译失败。
  所以这个构造函数Date是错误的,因为在使用默认参数时,必须在函数声明或定义中为这些参数提供默认值,否则编译器会报错。

class Date
{
public:
/*
// 如果用户显式定义了构造函数,编译器将不再生成
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
*/
void Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	// 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
	// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
	Date d1;
	return 0;
}

(6)C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int / char …,自定义类型就是我们使用 class / struct /union 等自己定义的类型,编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。

  Date 类包含了三个整型类型的基本数据成员 _year、_month、_day 和一个 Time 类型的私有数据成员 _t,其中 Time 类由于没有显式定义构造函数,因此会有一个默认的无参构造函数。

  在主函数中,创建对象 d 时将调用 Date 类的默认构造函数,并在其中对其基本数据成员 _year、_month、_day 进行了初始化,Time 类型的成员 _t 也会调用其默认构造函数,将 _hour、 _minute、 _second 分别初始化为 0。

class Time
{
public:
Time()
{
	cout << "Time()" << endl;
	_hour = 0;
	_minute = 0;
	_second = 0;
}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

  注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

class Time
{
public:
Time()
{
	cout << "Time()" << endl;
	_hour = 0;
	_minute = 0;
	_second = 0;
}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

(7)无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

  在这段代码中,Date 类显式声明了两个构造函数——其中一个是默认构造函数,另一个是带有三个参数的构造函数。在 Test() 函数中,没有提供任何参数来创建 Date 类型的对象 d1,因此将会使用默认构造函数进行初始化。

  所有这个测试函数能够通过编译,且会成功创建一个 Date 类型的对象 d1,该对象的年月日属性分别为默认值 1900 年 1 月 1 日。

class Date
{
public:
Date()
{
	_year = 1900;
	_month = 1;
	_day = 1;
}

Date(int year = 1900, int month = 1, int day = 1)
{
	_year = year;
	_month = month;
	_day = day;
}

private:
	int _year;
	int _month;
	int _day;
};

void Test()
{
	Date d1;
}

3.析构函数

3.1析构函数的概念

  在 C++ 中,析构函数(Destructor)是一种特殊的函数,其名称与类名称相同,但在名称前面加上一个波浪号(~)。析构函数与构造函数一样,也是一种特殊的成员函数,但它是在对象生命周期结束时自动调用的,并且只能有一个析构函数,且不能带有参数。

  析构函数通常用于清理对象所占用的资源,比如释放动态申请的内存、关闭打开的文件等。具体来说,析构函数的工作包括释放内存或资源、删除临时文件、清理诸如打开的文件和数据库连接等。

析构函数有一下特点:

(1)在析构函数中,一般要释放对象所使用的内存或资源,防止内存泄漏。

(2)在堆上分配内存的对象,必须在析构函数中释放。

(3)调用析构函数的顺序与调用构造函数的顺序相反,即先析构派生类对象,再析构基类对象。

(4) 如果一个类有成员变量是指针类型或者是其他类的对象,需要在析构函数中先释放这些成员变量占用的内存,然后再释放自己的内存。

(5) 如果一个类没有显示地声明析构函数,则编译器会为该类生成一个默认的析构函数。默认的析构函数什么也不做。

总结:析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

简单的析构函数使用例子:

#include<iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A constructor" << endl;
    }
    
    ~A()
    {
        cout << "A destructor" << endl;
    }
};

int main()
{
    A a;
    return 0;
}

3.2析构函数的特征

析构函数是特殊的成员函数,其特征如下:

(1)析构函数名是在类名前加上字符 ~。

(2)无参数无返回值类型。

(3)一个类只能有一个析构函数。 若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。

(4)对象生命周期结束时,C++编译系统系统自动调用析构函数。


以栈的实现为例:

typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
	_array = (DataType*)malloc(sizeof(DataType) * capacity);
	if (NULL == _array)
	{
		perror("malloc申请空间失败!!!");
		return;
	}
	_capacity = capacity;
	_size = 0;
}

void Push(DataType data)
{
	// CheckCapacity();
	_array[_size] = data;
	_size++;
}

// 其他方法...
~Stack()
{
	if (_array)
	{
		free(_array);
		_array = NULL;
		_capacity = 0;
		_size = 0;
	}
}

private:
	DataType* _array;
	int _capacity;
	int _size;
};

void TestStack()
{
	Stack s;
	s.Push(1);
	s.Push(2);
}

(5)编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

  在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

  因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。

  但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。

注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。

class Time
{
public:
~Time()
{
	cout << "~Time()" << endl;
}
	
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}
//程序运行结束后输出:~Time()

(6)如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。


这些就是C++中类和对象中构造函数和析构函数的简单介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉文章来源地址https://www.toymoban.com/news/detail-463687.html

到了这里,关于【C++】类与对象——六个默认成员函数、构造函数的概念和特征,析构函数的概念和特征的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】类与对象:6大默认成员函数------拷贝构造和赋值运算符重载

      拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰) ,在用已存在的类类型对象创建新对象时由编译器自动调用。 1. 拷贝构造函数是 构造函数的一个重载形式 ; 2. 拷贝构造函数的 参数只有一个且必须是类类型对象的引用 ,使用传值方式编

    2024年02月03日
    浏览(34)
  • 【C++那些事儿】深入理解C++类与对象:从概念到实践(下)| 再谈构造函数(初始化列表)| explicit关键字 | static成员 | 友元

    📷 江池俊:个人主页 🔥 个人专栏:✅C++那些事儿 ✅Linux技术宝典 🌅 此去关山万里,定不负云起之望 1.1 构造函数体赋值 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是

    2024年03月21日
    浏览(41)
  • 【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解

    🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。 🎯每日格言:每日努力一点点,技术变化看得见。 我们生活中也有继承的例子,例如:小明继承了孙老师傅做拉面的手艺。继承就是一种延续、复用的方式。C++为了提高代码的可复用性,引入了继承机制,

    2024年04月10日
    浏览(36)
  • 【C++精华铺】5.C++类和对象(中)类的六个默认成员函数

    目录 1. 六个默认成员函数 2. 构造函数 2.1 概念 2.2 默认构造 2.2.1 系统生成的默认构造 2.2.2 自定义默认构造函数  2.3 构造函数的重载 3. 析构函数 3.1 概念  3.2 系统生成的析构函数  3.3 自定义析构函数 4. 拷贝构造 4.1 概念  4.2 默认生成的拷贝构造(浅拷贝)  4.3 自定义拷贝构

    2024年02月13日
    浏览(57)
  • 【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 类的6个默认成员函数 构造函数 概念 构造函数的特性及用法 析构函数 概念 析构函数的特性及用法 结语 本篇主要内容:类的6个默认成员函数中的 构造函数 和 析构函数 进入到类和对象内容的第二节,上篇博客中介绍了

    2024年04月16日
    浏览(40)
  • 【C++】:类和对象(中)之类的默认成员函数——构造函数and析构函数

    如果一个类中什么成员都没有,简称为空类 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数 由于编译器的优化 我们未给_a赋值 这里是不会报

    2024年02月08日
    浏览(32)
  • 【C++】类和对象③(类的默认成员函数:拷贝构造函数 | 赋值运算符重载)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 拷贝构造函数 概念 拷贝构造函数的特性及用法 赋值运算符重载 运算符重载 赋值运算符重载 结语 本篇主要内容:类的6个默认成员函数中的 拷贝构造函数 和 赋值运算符重载 在上篇文章中我们讲到了类的默认成员函数的

    2024年04月17日
    浏览(34)
  • 【C++技能树】类的六个成员函数Ⅰ --构造、析构、拷贝构造函数

    Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。 在开始本章内容之前,先浅浅的了解一下this指针的概念.这对理解后面的内容有着很大的帮助. this指针 顾名思义就是这个指针,我们先来看看下面这段很简单的代码 首先创建了一个

    2024年02月02日
    浏览(38)
  • C++:类的六个默认成员函数

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C++》 本篇博客作为C++知识总结,我们来认识类的六个默认成员函数。 下面我主要以日期类作为示例显示。 构造函数 是一个特殊的成员函数,名字与类名相同,创建类类型对象时(实例化类)由编译器自动调用,以保

    2024年02月08日
    浏览(37)
  • c++6大默认成员函数(类与对象中)

    目录 默认成员函数 1.构造函数 1.1构造函数的概念  1.2自己写的构造函数以及怎样调用 1.2.1日期的实现 1.2.2栈的实现 1.3编译器生成的默认构造函数 1.3.1日期的实现 1.3.1栈的实现 1.3.3队列(两个栈实现一个队列)的实现 2.析构函数 2.1析构函数的概念 2.2自己写的析构函数 2.2.1日

    2024年02月07日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包