析构函数&&拷贝构造函数
大家好,我是芒果,一名非科班的在校大学生。对C/C++、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流
作者简介:
- CSDN C/C++领域新星创作者https://blog.csdn.net/chuxinchangcun?type=blog
- 掘金LV3用户 https://juejin.cn/user/1381426159953960
- 阿里云社区专家博主,星级博主,技术博主 https://developer.aliyun.com/profile/expert/5lkdbuggiiuhc
- 华为云云享专家 https://bbs.huaweicloud.com/community/myhomepage
3.析构函数
前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成
对象的一些资源清理工作
3.2 特性
析构函数是特殊的成员函数。
其特征如下:
- 析构函数名是在类名前加上字符
~
。 无参数
无返回值。- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,C++编译系统系统
自动调用析构函数
。
一个类有且只有一个析构函数,因为析构函数不支持函数重载(析构函数是无参数的所以不支持)
对于日期类:没有资源需要清理,所以Date类不实现析构函数也是可以的
typedef int DataType;
class SeqList
{
public :
//构造函数
SeqList (int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
//析构函数
~SeqList()
{
if (_pData)
{
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
int main()
{
SeqList s1(20);
SeqList s2;
return 0;
}
构造时:s1先构造
析构时:s2先析构 。析构是相反的顺序,因为栈是后进先出,先把s2清理掉。
- 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认 析构函数,对会自定类型成员调用它的析构函数。
class String
{
public:
String(const char* str = "jack")
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
class Person
{
private:
String _name;
int _age;
};
int main()
{
Person p;
return 0;
}
如果我们不写默认生成析构函数,和构造函数类似,对于内置类型成员变量不做处理,对于自定义类型成员变量会去调用它的析构函数.
4.拷贝构造函数
4.1 概念
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
那在创建对象时,可否创建一个与一个对象一某一样的新对象呢? 构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类型对象 创建新对象时由编译器自动调用。
//例如
Date(Date d) //传值传参err
{};
Date(const Date& d)//传引用
{};
4.2 特征
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且
必须使用引用传参
,使用传值方式会引发无穷递归调用。
class Date
{
public:
//默认构造函数 -全缺省构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
//加上const修饰,防止写错了 d._year = _year
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
若采用的是传值传参:会引发无穷递归调用
调用拷贝构造,需要先传参数,传值传参又是一个拷贝构造->调用拷贝构造,需要先传参数,传值传参又是一个拷贝构造…
传值传参为什么是拷贝构造?
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷 贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
字节序拷贝:把我的每个字节依次拷贝给你
class Date
{
public:
//构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
// 这里d2调用的系统生成的默认拷贝构造完成拷贝,d2和d1的值也是一样的。
Date d2(d1);
return 0;
}
浅拷贝可能存在问题:
例如:
class Stack
{
public:
//默认构造函数
Stack(int capacity = 4)
{
_a = (int*)malloc(sizeof(int) * _capacity);
if (_a == nullptr)
{
cout << "malloc fail\n" << endl;
exit(-1);
}
_top = 0;
_capacity = capacity;
}
void Push(int x) {};
//析构函数
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
Stack s1;
//拷贝复制
Stack s2(s1);
return 0;
}
分析:
总结:
默认生成拷贝构造:
1.内置类型成员,会完成按字节序拷贝(浅拷贝)
2.自定义类型成员,会调用它自己类的拷贝构造
拷贝构造我们不写时,生成的默认拷贝构造函数对于内置类型和自定义类型都会拷贝处理,但是处理细节不一样,这个和构造函数和析构函数是不一样的。
- 点赞
- 收藏
- 关注作者
评论(0)