一、构造函数和析构函数
概念:对象的初始化和清理是两个非常重要的安全问题,一个对象或变量没有初始化时,对其使用后果是未知的。同样使用完一个变量,没有及时清理,也会造成一定的安全问题。C++为了f给我提供这种问题解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成初始化和对象清理的工作。 对象初始化和清理工作是编译器强制我们要做的事情,即便你不提供初始化和清理工作,编译器也会给你增加默认操作,只是这个默认化操作不会做任何事情,所以编写类就应该提供初始化函数。
#include<iostream>
using namespace std;
class MyClass
{
public
//1-构造函数写法
//没有返回值 也不写void 函数名与类名相同
//可以有参数,可以发生重载
//由编译器自动调用,不需要手动调用,且编译器只会调用一次
MyClass()
{
cout << "MyClass的构造函数调用" << endl;
}
//2-析构函数写法
//没有返回值 不写void 函数名与类名相同 在函数名称面前加~
//不可以有参数,不可以发生重载
//由编译器自动调用,不要手动调用,编译器也只调用一次
~MyClass()
{
cout << "~MyClass的析构函数调用" << endl;
}
};
void test01()
{
MyClass myclass; //实列化对象
}
int main()
{
test01();
return 0;
}
二、构造函数分类及调用
1.分类
按照参数进行分类 有参构造函数 无参构造函数(默认构造函数)
按照类型进行分类 普通构造函数 拷贝构造函数
class PurSon
{
public:
PurSon() //默认构造
{
cout << "PerSon的默认构造函数调用" << endl;
}
PurSon(int age)//有参构造
{
m_Age = age;
cout << "PerSon的有参构造函数调用" << endl;
}
PurSon(const PurSon &p)//拷贝构造
{
m_Age = p.m_Age;
cout << "PurSon拷贝函数调用" << endl;
}
~PurSon()//析构
{
cout << "PurSon析构函数调用" << endl;
}
int m_Age;
};
拷贝:类名(const 类名 &n)
必须加上const 防止拷贝内容被不经意的修改
必须加上引用 & ,如果不加 & 参数为值传递将拷贝新的临时变量引起无限递归 2.调用
void test01()
{
//无参构造函数
PurSon P;
//有参构造函数
//1)括号法
PurSon p2(10);//有参构造调用
PurSon p3(p2);//拷贝构造函数调用
cout << "P3的年龄:" << p3.m_Age << endl;
//2)显示法
PurSon p4 = PurSon(10);//有参构造函数
PurSon p5 = PurSon(p4);//拷贝构造哈数
//3)隐式法
PurSon p6 = 10; //等价于 PurSon p6=PurSon(10);可读性不强 不建议使用
PurSon p7 = p6;
}
注意点:
PurSon p();//不可以用括号法,调用无参构造函数 原因PurSon p(),编译器认为是一个函数声明
PurSon(10);//匿名函数对象 特点:当前执行完后,系统就立即回收
PurSon(p5);//不可以调用拷贝构造函数来初始化匿名对象PurSon(p5)编译器认为代码为 PurSon p5;
三、拷贝构造函数的调用时机
#include<iostream>
using namespace std;
class PerSon
{
public:
PerSon()
{
cout << "PerSon的默认构造函数调用" << endl;
}
PerSon(int age)
{
m_Age = age;
cout << "PerSon的有参构造函数调用" << endl;
}
PerSon(const PerSon & p)
{
m_Age = p.m_Age;
cout << "PerSon拷贝函数调用" << endl;
}
~PerSon()
{
cout << "PerSon析构函数调用" << endl;
}
int m_Age;
};
//1.使用一个已经创建好的对象来初始化另一个对象
void test03()
{
PerSon p1(18);
PerSon p2(p1);
cout << "p2年龄为: " << p2.m_Age << endl;
}
//2.以值传递的方式 给函数的参数传值
void dowork(PerSon p)
{
}
void test04()
{
PerSon p1;
dowork(p1);
}
//3.以值的方式返回局部变量
PerSon dowork2()
{
PerSon p1;
return p1;
}
void test05()
{
PerSon p = dowork2();
}
int main()
{
test03();
return 0;
}
四、构造函数调用规则
1.默认情况下:系统会默认给我们写的类添加至少3个函数
1)默认构造函数(无参,函数体为空)
2)默认析构函数(无参,函数体为空)
3)默认拷贝构造函数对类中非静态成员属性简单值拷贝
2.如果用户定义了 有参构造函数,那么系统就不会提供默认构造函数了,但是依然会提供拷贝构造函数
3.如果用户定义了 拷贝构造函数,那么系统就不会提供其他的普通构造函数了
五、深拷贝和浅拷贝
如果类中有属性开辟到堆区,那么释放时候,由于浅拷贝问题导致堆区内容会被重复释放,程序down掉。 利用深拷贝来解决浅拷贝带来的问题。
#include<iostream>
using namespace std;
class PtrSon
{
public:
PtrSon(char * name, int age) //普通构造函数
{
cout << "Person的有参构造函数调用" << endl;
m_Name = (char *)malloc(strlen(name) + 1);//开辟空间
strcpy(m_Name, name);
m_Age = age;
}
PtrSon(const PtrSon & p) //定义拷贝构造
{
m_Age = p.m_Age;
//m_Name=p.m_Name 系统提供的浅拷贝
//利用深拷贝(用户自己定义的拷贝构造函数) 解决浅拷贝带来的问题
m_Name=(char *)malloc(strlen(p.m_Name) + 1);给拷贝的函数开辟新空间
strcpy(m_Name, p.m_Name);
}
~PtrSon() //提供析构函数
{
if (m_Name = NULL)
{
cout << "PtrSon的析构函数调用" << endl;
free(m_Name); //释放堆区所开辟的空间 如果用户没有提供深拷贝 则调用系统提供的默认浅拷贝
//会导致析构函数释放同一块堆区(p1、p2指向同一块内存)内存两次
m_Name = NULL;
}
}
char * m_Name; //char *堆区
int m_Age;
};
void test06()
{
PtrSon p1("Tom", 18);
cout << "p1的姓名:" << p1.m_Name << " 年龄:" << p1.m_Age << endl;
PtrSon p2(p1);
cout << "p2的姓名:" << p2.m_Name << " 年龄:" << p2.m_Age << endl;
}
int main()
{
test06();
return 0;
}