前言
本文讲述了构造函数和析构函数的概念以及对应的示例,以便加深理解。
一、基本概念
1 构造函数
构造函数用于初始化类的对象,可以由程序员自己定义,也可以由编译器定义(构造函数内为空)。
其特点:
1.没有返回类型。
2.函数名和类名相同。
3.可以有参数,也可以没有。
4.可以重载。
5.程序会自动调用。
按照参数分类:
1.无参构造函数
2.有参构造函数(包括拷贝构造函数)
3种构造方法:
1.括号法
2.显示法
3.隐式转换法
调用规则:
1.编译器会给类添加至少三个构造函数:1,默认构造函数;2,析构函数(空);3,拷贝构造函数(值拷贝):如果不写拷贝构造,编译器会自动生成一个拷贝构造函数,Name = c.Name;。
2.如果自己写了有参构造函数,编译器不会再提供默认构造函数,但还是会提供拷贝构造函数。
3.如果自己写了拷贝构造函数,编译器其他两种构造函数都不会再提供。
2 析构函数
析构函数用于销毁之前声明的类对象,系统自动调用,用~+类名声明。
1.析构函数不可以有参数。
2.析构函数无法重载。
二、示例
1. 构造函数和析构函数的简单使用
class Cat
{
public:
Cat()
{
cout << "喵是无参构造函数" << endl;
}
Cat(string name)
{
Name = name;
cout << "喵是有参构造函数" << endl;
}
//用于拷贝c的属性值,用常量引用的方式进行
Cat(const Cat &c)
{
Name = c.Name;
cout<< "喵是拷贝构造函数" << endl;
}
~Cat()
{
cout << "喵是析构函数" << endl;
}
string Name;//咪咪名字
};
void FindName()
{
//括号法
Cat cat1;//用默认构造函数时,不能加括号,否则编译器会当作函数声明。
Cat cat2(cat1);//拷贝构造函数调用
Cat cat3("小300");//有参构造函数调用。
Cat cat4(cat3);//拷贝构造函数调用
cout << "第一个猫的名字是:" << cat1.Name << endl;
cout << "第二个猫的名字是:" << cat2.Name << endl;
cout << "第三个猫的名字是:" << cat3.Name << endl;
cout << "第四个猫的名字是:" << cat4.Name << endl;
//显示法
Cat cat5;//默认构造函数
Cat cat6 = Cat("三号楼");//有参构造函数调用。
Cat cat7 = Cat(cat6);//拷贝构造函数调用
Cat("蹭铁棍");//匿名对象,会直接调用构造函数和析构函数,再去执行后面的内容。
//Cat(cat7);不要匿名初始化拷贝构造函数
cout << "第五个猫的名字是:" << cat5.Name << endl;
cout << "第六个猫的名字是:" << cat6.Name << endl;
cout << "第七个猫的名字是:" << cat7.Name << endl;
//隐式调用
string name8 = "来两根";
Cat cat8 = name8;
cout << "第八个猫的名字是:" << cat8.Name << endl;
}
int main()
{
FindName();
}
喵是拷贝构造函数
喵是普通有参构造函数
喵是拷贝构造函数
第一个猫的名字是:
第二个猫的名字是:
第三个猫的名字是:小300
第四个猫的名字是:小300
喵是无参构造函数
喵是普通有参构造函数
喵是拷贝构造函数
喵是普通有参构造函数
喵是析构函数
第五个猫的名字是:
第六个猫的名字是:三号楼
第七个猫的名字是:三号楼
喵是普通有参构造函数
第八个猫的名字是:来两根
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
2. 拷贝构造函数的调用
void eat1(Cat cat)
{
cout << cat.Name << "吃猫粮" << endl;
}
Cat eat2()
{
Cat cat("炸糊的栗子");
cout << cat.Name << "吃骨头" << endl;
return cat;
}
void FindName()
{
//使用一个已经创建的对象来初始化新的对象
Cat cat1("小300");//有参构造函数调用。
Cat cat2(cat1);//拷贝构造函数调用
cout << "第一个猫的名字是:" << cat1.Name << endl;
cout << "第二个猫的名字是:" << cat2.Name << endl;
//值传递传值
Cat cat3("烤糊的栗子");
eat1(cat3);
cout << "第三个猫的名字是:" << cat3.Name << endl;
//值方式返回局部对象
Cat cat4 = eat2();
cout << "第四个猫的名字是:" << cat4.Name << endl;
}
喵是普通有参构造函数
喵是拷贝构造函数
第一个猫的名字是:小300
第二个猫的名字是:小300
喵是普通有参构造函数
喵是拷贝构造函数
烤糊的栗子吃猫粮
喵是析构函数
第三个猫的名字是:烤糊的栗子
喵是普通有参构造函数
炸糊的栗子吃骨头
喵是拷贝构造函数
喵是析构函数
第四个猫的名字是:炸糊的栗子
喵是析构函数
喵是析构函数
喵是析构函数
喵是析构函数
3. 浅拷贝和深拷贝
深拷贝:在堆区开辟空间,完成拷贝。
浅拷贝:简单赋值拷贝的方式就属于浅拷贝,比如默认的拷贝构造函数。
class Cat
{
public:
Cat()
{
cout << "喵是无参构造函数" << endl;
}
//Cat(string name)
//{
// Name = name;
// cout << "喵是普通有参构造函数" << endl;
//}
Cat(string name, int age)
{
Name = name;
Age = new int(age);
cout << "喵是普通有参构造函数" << endl;
}
Cat(const Cat &c)
{
Name = c.Name;
//深拷贝,用new将传入Age的地址解引用然后在堆区重新创建一个空间,与cat3所指向的新内存空间就不一样了,不会发生析构函数重复释放报错的问题。
Age = new int(*c.Age);
cout<< "喵是拷贝构造函数" << endl;
}
~Cat()
{
if (Age != NULL)
{
delete Age;
Age = NULL;
}
cout << "喵是析构函数" << endl;
}
string Name;//咪咪名字
int *Age;//咪咪年龄
};
void FindName()
{
浅拷贝,仅发生值传递
//Cat cat1("小300");
//Cat cat2(cat1);
//
//cout << "第一个猫的名字是:" << cat1.Name << endl;
//cout << "第二个猫的名字是:" << cat2.Name << endl;
//深拷贝,用new在内存中开辟新空间
Cat cat3("小300",2);
Cat cat4(cat3);
cout << "第三个猫的名字是:" << cat3.Name << endl;
cout << "第三个猫的年龄是:" << *cat3.Age << endl;
cout << "第四个猫的名字是:" << cat4.Name << endl;
cout << "第四个猫的年龄是:" << *cat4.Age << endl;
}
int main()
{
FindName();
}
喵是普通有参构造函数
喵是拷贝构造函数
第三个猫的名字是:小300
第三个猫的年龄是:2
第四个猫的名字是:小300
第四个猫的年龄是:2
喵是析构函数
喵是析构函数