C++——类与对象2

ops/2025/3/10 1:59:29/

类的6个默认成员函数

C++中,当类为空的时候(没有成员),编译器就什么都不做吗?

其实不是的,这时,编译器就会自动生成6个默认成员函数:

那么,什么是默认成员函数呢?
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

 6个默认成员函数:

1.初始化和清理:构造函数,析构函数

2.拷贝复制:拷贝构造,赋值重载

3.取地址重载:普通对象和const取地址。

一:构造函数

1.构造函数主要是完成初始化的任务。

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;}
int main()
{Date d1;d1.Init(2021, 3, 9);Date d2;d2.Init(2024, 7, 6);return 0;
}

看上面,当我们定义了一个日期的类,我们可以定义一个初始化函数来实现对日期时间的设置,但是呢?这是不是有点麻烦,那么我们可不可以每次调用时之前,它就自动把日期设置初始化好?答案是有的,我们的构造函数就是为了这而生的。

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

特点:

ps:虽然它叫构造函数,但是它是不会开辟空间创建对象的,它的任务只是进行初始化。

1. 函数名与类名相同。

2. 无返回值。

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;}int main()
{Date d1();     //第一种  “Date d1(void)”: 未调用原型函数(是否是有意用变量定义的?)Date d2(2022,2,2);   //第二种return 0;
}

 

说明:

如果 使用第一种的话,是错误的,声明了d3函数,该函数无参,返回一个日期类型的对象。
它会出现上图的情况,这样调用寻找不到构造函数的,因为编译器会认为你再调用某个函数,所以会识别不到。

class Date
{
public:Date(){int _year=2022;int _month=2;int _day=2;
}private:int _year;int _month;int _day;}int main()
{Date d1;    return 0;
}

采用无参的时候,对象后面不用跟括号,否则就成了函数声明

 当我们没有写构造函数时,它会出现什么呢?会发现是随机值

这里我们先来认识:

内置函数/基本类型:语言本身定义的类型eg:int,char,double/指针等等

 自定义类型:用struct/class定义的类型

这里我们默认不写的话编译器就会自动生成,内置函数不做处理(虽然有些编译器会处理,但也只是个性化行为,不是所有的都是),自定义函数会自动去调用默认函数。

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

4. 构造函数可以重载。

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

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 d1;return 0;
}

解释:隐藏了构造函数后,那么Date d1还能正常运行吗?

将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函

 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再
生成
无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用 

在vs2022中, 

我们可以发现:内置类型函数都要缺省值,且初始化符合我们的要求 

发现:全是自定义类型的构造,且这些类型都默认构造函数。 

不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

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

 简单来说:这里我们默认不写的话编译器就会自动生成,内置函数不做处理(虽然有些编译器会处理,但也只是个性化行为,不是所有的都是),自定义函数会自动去调用默认函数。

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

class Date
{
public:Date()   //1个{_year = 1900;_month = 1;_day = 1;}Date(int year = 1900, int month = 1, int day = 1)  //2个{_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
}

上面出现了2个构造函数,这是不可以的,只能出现一个。

二:析构函数:

1.什么是析构函数呢?

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

特性:

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

2. 无参数无返回值类型。

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

ps:自动生成默认的析构函数:这里跟构造函数一样:内置函数不做处理,自定义函数会自动去找默认析构函数。

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

这里以之前写的栈的销毁函数为例:

class Stack
{
public:Stack(int Decapacity=4){cout << "Stack()" << endl;_a = (int*)malloc(sizeof(int) * Decapacity);if (_a == nullptr){perror("malloc fail");return;}_top = _capacity = 0;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}/*void Destory(){free(_a);_a = nullptr;_top = _capacity = 0;}*/
private:int* _a;int _top;int _capacity;
};int main()
{Stack st;return 0;

上面我们可以检验出来,构造函数和析构函数都是会自动去找的,不用特意去调用。

什么时候需要些析构函数?什么时候又不需要呢?

1.一般情况下,又动态申请的都需要显示写析构函数来释放资源

2. 没有动态申请的,就不需要写。

3.需要释放资源的成员都是自定义函数,则不需要写。

上面,为什么在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类生成的默认析构函数

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

三:拷贝构造函数

什么是拷贝构造函数?

我们从
可以理解成“复制”的功能,生成一一个一模一样的产物。(双胞胎)

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
 

特性:

1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

Date (const Date& d)
{_year=d._year;_month=d._month;_day=d._day;
}

若没有以引用,则会出现无限递归的情况。 

 

3.ps:C++中,拷贝构造:内置类型直接拷贝,自定义类型必须要调用拷贝构造。 

4.. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按

字节序完成拷贝(如:内置类型),这种拷贝叫做浅拷贝,或者值拷贝

编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。但是像其他的是需要的:(如栈的拷贝构造)

1.Date和MyQueue不需要我们写

3.Stack需要我们写
 

class Stack
{
public:Stack(int Decapacity = 4){_a = (int*)malloc(sizeof(int) * Decapacity);if (_a == nullptr){perror("malloc fail");return;}_size = 0;_capacity = Decapacity;}int Push(int x){return _a[_size] = x;_size++;}~Stack(){free(_a);_a = nullptr;_size = _capacity = 0;}
private:int* _a;int _size;int _capacity;
};int main()
{Stack st;st.Push(1);st.Push(2);st.Push(3);st.Push(4);Stack st1(st);return 0;
}

这里为什么会崩溃呢?

答:因为这里st调用构造函数创建了4个空间之后,存了1,2,3,4数字。

当用st去拷贝构造st1时,Stack没有显示拷贝构造函数,所以他会自动去找默认拷贝构造函数,因为它们是内置类型,直接去拷贝的,即将st的值原封不动的拷贝到st1里,这是st和st1就会指向同一个空间,到了结束的时候,调用到了析构函数,首先要销毁的是st1,已经将st1的空间释放了,但是st并不知道已经释放了,所以它也会再次释放掉,就会重复释放问题,即一个空间多次释放,所以会崩溃。

 

所以,我们要想成功运行,必须存的时候不是在同一块空间。即:创建一个临时是数组空间。

Stack(Stack& s)
{_a = (int*)malloc(sizeof(int) * s._capacity);if (_a == nullptr){perror("malloc fail");return;}memcpy(_a, s._a, sizeof(int) * s._capacity);_size = s._size;_capacity = s._capacity;
}

 好了,本次的分享到此结束了。

最后来到我们本次鸡汤部分:

走自己的路,看自己的风景,活著自己生命的节奏。


http://www.ppmy.cn/ops/164534.html

相关文章

Python 智能机房签到系统:高效管理课堂考勤

智能机房签到系统&#xff1a;高效管理课堂考勤 相关资源文件已经打包成EXE文件&#xff0c;可双击直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Python相关程序案例&#xff0c;秉着开源精神的想法&#xff0c;…

UDP学习笔记(一)为什么UDP需要先将数据转换为字节数组

UDP 发送数据时需要先将数据转换为字节数组再发送&#xff0c;主要是因为计算机网络传输的最基本单位是“字节”&#xff08;Byte&#xff09;。让我们从以下几个方面来深入理解这个设计选择&#xff1a; 1. 计算机网络只能传输“字节” 在网络通信中&#xff0c;无论是 TCP 还…

ubuntu 20.04 C++ 源码编译 cuda版本 opencv4.5.0

前提条件是安装好了cuda和cudnn 点击下载&#xff1a; opencv_contrib4.5.0 opencv 4.5.0 解压重命名后 进入opencv目录&#xff0c;创建build目录 “CUDA_ARCH_BIN ?” 这里要根据显卡查询一下,我的cuda是11&#xff0c;显卡1650&#xff0c;所以是7.5 查询方法1&#xff1…

Windsuf 连接失败问题:[unavailable] unavailable: dial tcp...

问题描述 3月6日&#xff0c;在使用Windsuf 时&#xff0c;遇到以下网络连接错误&#xff1a; [unavailable] unavailable: dial tcp 35.223.238.178:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of…

2025 年开源替代方案为何正在取代 OutSystems?技术自由度与成本优势深度解析

原文链接&#xff1a;https://www.nocobase.com/cn/blog/outsystems-open-source-alternatives OutSystems 的隐藏成本不只是金钱 OutSystems 是企业低代码开发领域的领军者。通过将生成式 AI 工具深度集成到软件生命周期&#xff0c;OutSystems 助力企业快速构建客户导向的门…

DeepSeek集成到VScode工具,让编程更高效

DeepSeek与VScode的强强联合&#xff0c;为编程效率树立了新标杆。 DeepSeek&#xff0c;一款卓越的代码搜索引擎&#xff0c;以其精准的索引和高速的检索能力&#xff0c;助力开发者在浩瀚的代码海洋中迅速定位关键信息。 集成至VScode后&#xff0c;开发者无需离开熟悉的编辑…

AI正逐渐进入科技+非科技的深水区

随着AI的快速发展&#xff0c;人工智能正逐渐进入一个融合科技与非科技领域的复杂系统。在传统上&#xff0c;AI主要依赖于科学技术&#xff0c;如算法、计算力和大数据等&#xff0c;来处理和分析信息&#xff0c;进行预测和决策。然而&#xff0c;随着AI的应用范围不断扩展&a…

用友 U8出入库查询SQL 连接UNION ALL

-- 销售出库单查询 SELECT 销售出库单 AS 单据类型, a.cCode AS 单号, a.dDate AS 日期, a.cMaker AS 制单人, a.cHandler AS 审核人, a.dVeriDate AS 审核日期, b.cInvCode AS 存货编码, b.iQuantity AS 数量, b.cBatch AS 批号, c.…