类的默认成员函数
1. 构造函数
初始化类
-
函数名与类名
相同
- 无返回值
-
对象实例化时
编译器自动调用对应的构造函数
-
构造函数可以
重载
我们测试一下对象实例化时
编译器自动调用对应的构造函数
#include <iostream>
using namespace std;
class Date
{
public:
//含参的构造函数
Date(int year = 0, int month = 0, int day = 0)
{
_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.print();
return 0;
}
测试一下构造函数可以
重载
class Date
{
public:
Date()
{
_year = 0;
_month = 0;
_day = 0;
}
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//这里使用的时候存在歧义
Date d2(2022,1,21);
return 0;
}
可以构成重载,但能否使用另外说明
可以改成
Date()
{
_year = 0;
_month = 0;
_day = 0;
}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//上面两个函数其实等价于下面一个函数
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
2. 默认构造函数
如果我们自己没有写构造函数,类会自己生成一个构造函数初始化
但数据显示根本没有给我初始化0
原因:C++中类分为两个内置类型(基本类型),自定义类型
-
内置类型:
int char double
指针 数组等 -
自定义类型:
struct class
定义的类型
如果我们不写构造函数,编译器默认生成构造函数,并且
对内置类型不作初始化处理
,而对自定义类型,会去寻找
自定义类型的默认构造函数(没有参数就可以访问的构造函数),如果没有则会报错
有默认构造函数时,没有错误,无默认构造函数时,(全缺省可以)半缺省以及要传参数会出错
所以有三种:
不会报错的
- 全缺省
- 无参
- 不写(程序自动生成,但是无用默认构造函数(什么都不做))
-
但默认构造函数,有且只能有一个
歧义:
可能你想不传参定义对象,但这里对象创建的时
d1()
例子:在类里面定义一个自定义类型的
成员变量st
当我们一步一步调试的时候,
在初始化类里面内置类型之前,先初始化了自定义类型的成员变量st
#include <iostream>
using namespace std;
class st
{
public:
st()
{
a = 0;
b = 0;
}
private:
int a;
int b;
};
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
st ST;
};
int main()
{
Date d1;
return 0;
}
3. 析构函数
对象在销毁时会自动调用析构函数
- 析构函数名是在类名前加上字符 ~
-
无参数无返回值
-
一个类有且只有一个析构函数
。若未显式定义,系统会自动生成默认的析构函数。 (因为没有参数,所以不构成函数重载) -
对象生命周期结束时
,C++编译系统系统自动调用析构函数。比如
return 0
的时候,看的是定义
用栈去解释
class Stack
{
public:
//初始化栈
Stack(int capacity = 4)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (_a = nullptr)
{
cout << "malloc fail" << endl;
exit(-1);
}
_capacity = capacity;
_top = 0;
}
//销毁栈
~Stack()
{
free(_a);
_a = nullptr;
_capacity = 0;
_top = 0;
}
private:
int* _a;//数组,动态栈
int _top;//栈顶
int _capacity;//栈大小
};
int main()
{
Stack d1;
Stack d2;
return 0;
}
-
因为是栈,后进先出,
初始化顺序先d1后d2,销毁顺序先d2,后d1
再利用上面年月日的例子
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
return 0;
}
可以在运行的时候发现,
在
Date d1;
走完之后,年月日都为
1
(构造函数)
,
在
return 0;
之前,年月日变为0
,将析构函数删除后,不会变为
0
,编译器并没有像析构函数那样帮我们销毁(重新初始化)
可见析构函数跟构造函数一样,不写的话,默认构造函数/析构函数不会起什么作用,同样是内置类型不作处理,自定义类型需要调用对应的析构函数。
关于为什么C++不作处理
像指针
int* a;//普通指针
FILE* x;//文件指针
如果free掉文件指针会出大问题,有些是不需要它free的,所以对内置类型不作处理,对自定义类型需要默认的析构函数
4. 拷贝/复制构造函数
-
首先
它和类名相同,没有返回值
,这就意味着它跟构造函数构成函数重载 -
参数只有一个,并且是引用传参
,否则会造成无限拷贝构造(传值需要拷贝) -
传引用不需要调用拷贝构造
Date(const Date& d)
{
_year = d._year;
_day = d._day;
_month = d._month;
}
- 对于内置类型,按字节拷贝(浅拷贝)
- 如果成员有是数组指针,
Stack st1;
// 拷贝复制
Stack st2(st1);
-
这里
st2
拷贝了
st1
的指针,当修改其数组中元素时,
st1
也会被修改,我们需要的是独立的类 -
如果内置类型是指针,如果是动态开辟的指针,不能被
free
两次 -
但如果自己不写
free
,默认的析构函数不会去
free
动态开辟的指针 - 自定义类型调用自己的拷贝构造