C++类和对象(二)构造函数、析构函数、拷贝构造函数
创始人
2024-03-16 04:20:48

目录

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

2. 构造函数

2.1 概念

2.2 特性

3.析构函数

3.1 概念

3.2 特性

4. 拷贝构造函数

4.1 概念

4.2 特征


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

如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
class Date {}; //空类

2. 构造函数

2.1 概念

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(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成 员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2 特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象其特征如下: 1. 函数名与类名相同。 2. 无返回值。 3. 对象实例化时编译器自动调用对应的构造函数。 4. 构造函数可以重载 5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
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();
}
       C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如: int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值
class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参 构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数 因为你不传参,我另一个这边有默认值,两个有一个就能完成工作,如下代码:全缺省的已经可以包含无参的功能,两个都在的话,创建对象可能出现问题
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;
};

3.析构函数

3.1 概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

3.2 特性

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

1. 析构函数名是在类名前加上字符 ~。 2. 无参数无返回值类型。 3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载 4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

以下为栈的实现

#include
using namespace std;
#include
#includetypedef int DataType;struct Stack
{
public:void Init(){_array = (DataType*)malloc(10 * sizeof(DataType));if (NULL == _array){assert(false);return;}//没上面问题容易给3,数量为0_capacity = 10;_size = 0;}//入栈void Push(DataType data){_array[_size] = data;++_size;}//出战void Pop(){if (Empty())return;_size--;}//获取栈顶元素DataType Top(){assert(!Empty());return _array[_size - 1];}bool Empty(){return 0 == _size;}int Size(){return _size;}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private://扩容方法void _CheckCapacity(){}
private:DataType* _array;size_t _capacity;size_t _size;
};int main()
{Stack s;s.Init();s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(5);cout << s.Top() << endl;cout << s.Size() << endl;s.Pop();s.Pop();cout << s.Top() << endl;cout << s.Size() << endl;s.Destroy();return 0;
}

因为我们代码中自己写了初始化 Init 和 Destory,所以在主函数中你要进行栈操作,必须手动加上s.Init();以及s.Destroy(); 要是不加就会崩溃,这样的话你总有失误的时候,此时我们想让栈创建好就有空间,所以对代码需要优化,下面为部分优化代码

	~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
void TestStack()
{Stack s;s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(66);cout << s.Top() << endl;cout << s.Size() << endl;s.Pop();s.Pop();cout << s.Top() << endl;cout << s.Size() << endl;
}
int main()
{TestStack();return 0;
}
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

注意:像Date类一样,对象中没有涉及到任何资源管理时,该类的析构函数可以不必给出

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};
void TestDate()
{Date d(2022, 11, 22);
}
int main()
{void TestDate();return 0;
}

4. 拷贝构造函数

4.1 概念

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

4.2 特征

拷贝构造函数也是特殊的成员函数,其特征如下 1. 拷贝构造函数是构造函数的一个重载形式。 2. 拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用
Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date d2(d1);

错误例子如下:如果这样使用,会不断的调用拷贝,这就是传值引发对象的拷贝

Date(const Date& d)

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

问题:编译器生成的拷贝构造,虽然没有生成但是可以完成拷贝构造的工作,既然编译器已经可以完成了,那拷贝构造函数还需要用户自己写吗?、

答:像日期这种没有涉及到资源管理时, 可写可不写,一般不写,因为编译器可以完成拷贝的工作,如果需要自己再去实现,注意编译器是按照值的方式拷贝的---即:将一个对象中的内容原封不动的拷贝到另一个对象中(浅拷贝)

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构 造函数是一定要写的,否则就是浅拷贝

 

注意:

// 1. 以值的方式返回时,如果返回的是匿名对象,则编译器不会再用匿名对象

// 拷贝构造临时对象,而是直接将匿名对象返回了

// 匿名对象:没有名字的对象

// 2. 如果参数是以值的方式传递,实参如果也是匿名对象,也会少一次拷贝构造

// 3. 在C++中,参数能使用引用尽量使用引用,如果不想通过形参改变外部的实参,尽量设置为const类型的引用

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...