C++构造和析构
文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、构造函数
什么是构造函数
-
函数名和类名相同
没有返回值
print() -
如果不写构造函数,任何类中都存在一个默认的构造函数
默认的构造函数是无参的
在这里插入代#include <iostream>
#include <string>
using namespace std;
class student
{
public:
void print()
{
cout << name << " " << age << endl;
}
protected:
string name = "lisa";
int age = 18;
};
int main()
{
student stu;
stu.print();
while (1);
return 0;
}
运行结果:
- 当我们自己写了构造函数,默认的构造函数就不存在
#include <iostream>
#include <string>
using namespace std;
class student
{
public:
student(string sName, int sAge)
{
name = sName;
age = sAge;
}
void print()
{
cout << name << " " << age << endl;
}
protected:
string name = "lisa";
int age = 18;
};
int main()
{
//student stu;构造无参的对象,需要无参构造函数
//stu.print();
student stu("lisi", 18);
while (1);
return 0;
}
运行结果:
构造函数在构造对象的时候调用
在这里插入代码片
-
delete可以用来删掉默认的函数 删掉默认的构造函数意味着不能构造对象
student() = delete; -
指定使用默认的无参构造函数,用default说明
-
允许构造函数调用另一个构造函数,只是要用初始参数列表的写法
避免形参名和数据成员名字相同的导致问题
#include <iostream>
#include <string>
using namespace std;
class call
{
public:
call(string name, int age) :name(name), age(age){}
//委托构造:允许构造函数调用另一个构造函数
call() :call{ "默认", 18 }{} //没有给数据初始化
void print()
{
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
int main()
{
call test;
test.print();
while (1);
return 0;
}
运行结果:
- 初始化参数列表:只有构造函数有
构造函数名(参数1,参数2…):成员1(参数1),成员2(参数2)…
//初始化参数列表的写法
string name = "lisi";
class list
{
public:
list(string sname=" ", int sage=18) :name(sname), age(sage)
{
cout << "初始化参数列表" << endl;
//继承和类的组合必须采用初始化参数列表写法
}
list(int age) :name(name), age(age){} //通过全局变量初始化参数列表
protected:
string name;
int age;
};
构造函数用来做什么
- 构造函数用来构造对象
构造函数更多是用来初始化数据成员
思考题
为什么不写构造函数可以构造对象?
是因为存在一个默认的无参构造函数,所以可以构造无参对象
构造函数重载是为了什么?
为了构造不同长相的对象
#include <iostream>
#include <string>
using namespace std;
//为了能够构造不同长相的对象,我们会给构造函数缺省处理
class Teach
{
public :
Teach(string tname=" ", int tage=19)
{
name = tname;
age = tage;
}
//上面函数等效可以实现下面三个函数的功能
//Teach(){}
//Teach(string sName){ name = sName; }
//Teach(string sName, int sAge){ name = sName, age = sAge; }
protected:
string name;
int age;
};
int main()
{
Teach teach1;
Teach teach2("张三");
Teach teach3("李四",18);
while (1);
return 0;
}
二、析构函数
1.析构函数是什么样的
- 无返回值
- 无参数
- 函数名:-类名
- 不写的话会存在默认的析构函数,析构函数不能重载
- 析构函数不需要自己调用,对象消失之前会调用析构函数
#include <iostream>
using namespace std;
class Student
{
public:
Student(const char* pstr, int age) :age(age)
{
str = new char[strlen(pstr) + 1];
strcpy_s(str,strlen(pstr)+1, pstr);
}
void print()
{
cout << str << "\t" << age << endl;
}
~Student();
protected:
char *str;
int age;
};
Student::~Student()
{
cout << "我叫做析构函数" << endl; //释放内存放在析构函数内完成
delete[]str;
}
int main()
{
{
Student student("张三", 18);
student.print();
}
cout << "主函数" << endl;
//new一个对象的时候,只有delete才会调用析构函数
{
Student*pObject = new Student("zhangsan", 12);
delete pObject;
pObject = nullptr;
}
while (1);
return 0;
}
运行结果:
2.析构函数用来做什么?(什么时候需要自己手动写析构函数)
- 当类中的数据成员是指针的时候,并且动态申请内存就需要手写析构
- 析构函数用来释放数据成员申请动态内存
三、拷贝构造函数
1.拷贝构造函数也是构造函数
- 拷贝构造函数长相和构造函数一样的,只是参数是固定
拷贝构造函数唯一的参数是对对象引用 - 不写拷贝构造函数,也存在一个默认的拷贝构造函数
- 拷贝构造函数作用: 通过一个对象去初始化另一个对象
2. 问题?
什么时候调用拷贝构造?
当通过一个对象去创建出来另一个新的对象时候需要调用拷贝
- 拷贝构造什么时候需要加const修饰参数?
当存在匿名对象赋值操作的时候,必须要const修饰
#include <iostream>
#include <string>
using namespace std;
class Student{
public:
Student() = default;
Student(const char* name, int age) :name(name), age(age){}
void print()
{
cout << name << "\t" << age << endl;
}
//拷贝构造
Student(const Student& lisi)
{
name = lisi.name; //zhangsan.name=lisi.name
age = lisi.age; //zhangsan.name=zhangsan.name
cout << "拷贝构造" << endl;
}
protected:
string name;
int age;
};
void printData(Student lisi) //student lisi=实参
{
cout << "拷贝构造" << endl;
lisi.print();
}
void printData2(Student &lisi) //不存在拷贝构造,实参就是形参 形参就是实参
{
lisi.print();
}
int main()
{
Student lisi("lisi", 18);
lisi.print();
//显式调用
cout << "显式调用" << endl;
Student zhangsan(lisi); //通过一个对象创建另一个对象
zhangsan.print();
//隐式调用
cout << "隐式调用" << endl;
Student wangwu = lisi; //拷贝构造
Student xiaoming;
xiaoming = lisi; //运算符重载
xiaoming.print();
//函数传参
cout << "第一种调用形态" << endl;
printData(lisi);
cout << "第二种调用形态" << endl;
printData2(lisi);
//无名对象 匿名对象临时对象是一个常属性的
Student temp;
temp = Student("匿名", 18);
temp.print();
//匿名对象创建对象的时候,拷贝构造一定要用const修饰 匿名对象是一个右值
Student temp2 = Student("匿名", 19);
while (1);
return 0;
}
运行结果:
深浅拷贝
- 浅拷贝: 默认的拷贝构造叫做浅拷贝
- 深拷贝:拷贝构造函数中做了new内存才做,并且拷贝赋值的操作
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
class Student
{
public:
Student(const char* mname, int age) :age(age)
{
name = new char[strlen(mname) + 1];
strcpy_s(name, strlen(mname) + 1, mname);
}
void print()
{
cout << name << "\t" << age <<endl;
}
Student(const Student& object) //深拷贝
{
//name=object.name;
name = new char[strlen(object.name) + 1];
strcpy_s(name,strlen(object.name)+1, object.name);
age = object.age;
}
~Student()
{
delete[] name;
}
protected:
char* name;
int age;
};
int main()
{
{
//浅拷贝 运行时会重复释放
Student lisi("lisi", 20);
Student zhangsan(lisi);
Student wangwu = lisi;
lisi.print();
zhangsan.print();
wangwu.print();
}
while (1);
return 0;
}
4、构造和析构顺序问题
- 普通对象,构造顺序和析构顺序是相反的
- new出来的对象,delete会直接调用析构函数
- static对象,当程序释放的时候,生命周期才结束,所以是最后释放
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student(string name = "x") :name(name) {
cout << name;
}
~Student(){
cout << name;
}
protected:
string name;
};
int main()
{
{
Student stu1("A"); //A
static Student stu2("B"); //B 程序关闭时候才死亡,最后析构
Student* stu3 = new Student("C"); //C
Student stu4[4]; //xxxx
delete stu3; //C delete 直接调用析构
stu3 = nullptr;
//xxxxAB
}
while (1);
return 0;
}
运行结果:
ABCxxxxCxxxxAB
C++结构体
#include <iostream>
#include <string>
using namespace std;
struct Student
{
//默认为公有属性
//类中默认属性是私有属性
//protected:
string name;
int age;
public:
Student(string name) :name(name)
{
cout << "构造函数" << endl;
}
Student(const Student& object)
{
name = object.name;
age = object.age;
cout << "拷贝构造" << endl;
}
~Student()
{
}
};
int main()
{
//采用创建时候赋值的方式,也是调用构造函数
//MM object = { "lisa",19 }; 错误,因为没有两个参数的构造函数
Student object = { "lisa" };
cout << object.name << "\t" << object.age << endl;
//C++结构体一旦写了构造函数,就必须按照C++类的方式的去用
Student mm(object);
cout << mm.name << "\t" << mm.age << endl;
return 0;
}
运行结果: