一、构造函数
1.定义
构造函数是类的成员函数,函数名与类名相同,在创建类类型对象时,由编译器自动调用,一个对象在被创建时,会调用一次构造函数,此后,不会在调用构造函数,即就是构造函数是在对象的整个生命周期里只会被调用一次
2.构造函数的书写格式
3.构造函数的特性
(1)构造函数的函数名与类名相同;
(2)构造函数有初始化列表;
(3)构造函数由编译器自动调用;
(4)构造函数的调用时机:在对象创建时才会被调用,并且在对象的整个生命周期中只存在一次;
(5)构造函数没有返回值,但是有参数;
ps:其实构造函数是有返回值的,返回的是当前对象的地址
(6)构造函数没有显式定义时,编译器会提供一个默认的构造函数
ps:编译器合成默认构造函数有四个场景,具体参考博客:
(7)构造函数不能用const修饰;
const在修饰函数时,实际是在修饰隐含的this指针,表示在此函数中不可以对类的任何成员进行修改,而构函数恰恰是用来构造对象的,所以构造函数不能用const修饰;
(8)构造函数可以重载,实参决定了调用那个构造函数;
(9)无参构造函数和带有缺省值(此处是全缺省)的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个,即就是无参的构造函数和带有缺省值的构造函数只能存在一个;
(10)构造函数不能为虚函数
ps:无参构造函数
class Stu
{
public:
Stu()
{}
private:
int _id;
int _grade;
char _name[5];
};
带有缺省值的构造函数
4.构造函数的作用
(1)初始化对象;
(2)构建对象;
(3)类型转换(此时的构造函数一定要是单参的,也可以是多个参数,只要带着缺省值就好)
5.初始化列表
(1)初始化列表的格式
以一个冒号开始,接着是一个逗号分隔的数据成员列表,每个数据后面圆括号中的值就是该变量的初始值,如上图 ,就是一个初始化列表
(2)作用
是用来初始化对象的
(3)特性
a.每个数据成员在初始化列表里只能出现一次;
b.初始化列表只是用来初始化成员的,并不指定这些成员的初始化顺序,数据成员在类中的定义顺序才是参数列表的初始化顺序
ps:在初始化变量时,尽量与变量的声明顺序一致,避免造成不必要的错误
(4)类中以下成员必须要在初始化列表中定义:
a、引用数据成员
因为引用类型的变量必须初始化,而如果在函数体中才开始初始化的话,此时就是赋值,不是初始化了,所以对引用成员必须放在初始化列表中初始化
b、const修饰的数据成员
c、类类型数据成员
6.关键字explicit
(1)用此关键字修饰构造函数的话,会抑制由构造函数定义的隐式转换
(2)explicit使用位置
当创建类成员变量时,加上此关键字就可以了,在类外就不需要在重复了
二、拷贝构造函数
1.定义
此函数只有单个形参,而且该参数的类型是对本类类型的引用(常用const修饰)
ps:拷贝构造函数是特殊的构造函数,创建对象时使用已经存在的同类对象来进行初始化
2.拷贝构造函数的格式
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
Stu(int id=10, int grade=100,char*name="张三")
:_id(id)
, _grade(grade)
{
strcpy(_name, name);
cout << _id << "," << _grade << "," << _name << endl;
}
Stu(const Stu&s)
{
_id = s._id;
_grade = s._grade;
strcpy(_name, s._name);
}
private:
int _id;
int _grade;
char _name[5];
};
int main()
{
Stu s1(1, 99,"李四");
Stu s2(2);
Stu s3;
Stu s4(s1);
system("pause");
return 0;
}
3.拷贝构造函数的调用时机
在构建对象时,由编译器自动调用
4.拷贝构造函数的参数类型为什么是引用?
ps:如果不传引用,程序会崩溃,导致栈溢出
5.拷贝构造函数的特性
(1)由编译器自动调用;
(2)是构造函数的重载,所以拷贝构造函数的函数名与类名相同;
(3)只有单个形参,且此参数是类类型的引用,常用const修饰;
(4)构造函数的特性,拷贝构造函数都有,只有一个特性拷贝构造函数是没有的,就是构造函数可以重载,拷贝构造函数可以重载;
(5)拷贝构造函数不能重载;
(6)如果没有显式定义,系统会自动合成一个默认的拷贝构造暗黑少女胡,合成的拷贝构造函数会依次拷贝类的数据成员,完成相关对象的初始化;
ps:系统合成默认的拷贝构造函数是有具体的场景的(场景与系统自动合成默认构造函数的场景类似),并不是什么时候都会合成。
6.使用场景
(1)对象实例化对象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
Stu(int id=10, int grade=100,char*name="张三")
:_id(id)
, _grade(grade)
{
strcpy(_name, name);
cout << _id << "," << _grade << "," << _name << endl;
}
Stu(const Stu&s)
{
_id = s._id;
_grade = s._grade;
strcpy(_name, s._name);
cout << _id << "," << _grade << "," << _name << endl;
}
private:
int _id;
int _grade;
char _name[5];
};
int main()
{
Stu s1(1, 99,"李四");
Stu s2;
Stu s3(s2);
system("pause");
return 0;
}
运行结果:
(2)传值方式作为函数的参数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
Stu(int id=10, int grade=100,char*name="张三")
:_id(id)
, _grade(grade)
{
strcpy(_name, name);
cout << _id << "," << _grade << "," << _name << endl;
}
Stu(const Stu&s)
{
_id = s._id;
_grade = s._grade;
strcpy(_name, s._name);
}
void SetInfo(const Stu s){
_id = s._id;
_grade = s._grade;
strcpy(_name, s._name);
cout << _id << "," << _grade << "," << _name << endl;
}
private:
int _id;
int _grade;
char _name[5];
};
int main()
{
Stu s1(1, 99,"李四");
Stu s2;
Stu s3(s2);
Stu s4;
s4.SetInfo(s1);
system("pause");
return 0;
}
运行结果:
(3)传值方式作为函数的返回值
三、析构函数
1.定义
与构造函数相反,析构函数是用来销毁对象的
2.析构函数的调用时机
在对象被销毁时,由编译器自动调用
3.析构函数的作用
销毁对象,完成类的一些清理和汕尾工作
4.析构函数的格式
析构函数名与构造函数名相同,只不过在函数名之前多加了一个符号:~
~Stu()
{
需要销毁的类的成员
}
5.析构函数的特性
(1)析构函数名=在类名之前多加一个字符:~
(2)析构函数无参数无返回值
(3)在对象生命周期结束时,析构函数由编译器自动调用
(4)一个类只能有一个析构函数,若析构函数没有显式定义,系统会自动合成一个缺省的析构函数ps:只有当涉及到资源的清理的时候,系统才会合成一个析构函数
(5)析构函数不能被重载,因为析构函数没有参数
(6)析构函数不是删除对象,而是做一些资源清理工作
编译器合成构造函数的情况:https://blog.csdn.net/dangzhangjing97/article/details/78597264