一、写在前面
1.C++的内存划分
(1)全局数据区:存放全局变量、常量、静态数据
(2)代码区:存放程序的代码
(3)栈区:存放局部变量、函数的参数、返回数据、返回地址等
(4)堆区(自由存储区):作为其他操作的使用的资源
注意:当我们的程序new或者malloc申请到了一些堆内存时,在使用完之后务必回收堆内存,否则会造成内存泄露!
2.malloc/free与new/delete
(1)用malloc/free来申请堆内存和回收堆内存
class Student {
public:
Student() {};
Student(char* name, int num) {
strncpy_s(this->name, name, sizeof(this->name));
this->num = num;
}
char* getName() {
return this->name;
}
void setName(char* name) {
strncpy_s(this->name, name, sizeof(this->name));
}
int getNum() {
return this->num;
}
void setNum(int num) {
this->num = num;
}
private:
char name[20];
int num;
};
void main() {
//用stu1类指针指向一个内存大小为sizeof(Student)的堆空间内存地址
Student* stu1 = (Student*)malloc(sizeof(Student));
//回收堆内存
free(stu1);
//stu2类指针指向在堆创建的对象地址
Student* stu2 = new Student("考拉啦",1003);
//输出stu2对象名字
cout<< stu2->getName() << endl;
//回收堆内存
delete(stu2);
}
(2)两者的区别
用new开辟堆内存会调用类的构造函数,但是用malloc开辟堆内存不会调用类的构造函数
二、堆对象和堆对象数组
1.创建堆对象和回收堆内存
Student* stu2 = new Student("考拉啦",1003);
delete stu2;
2.创建堆对象数组和回收堆内存
//在堆创建一个数组长度为10的对象数组
Student* pStuArr = new Student[10];
//回收堆内存
//delete pStuArr; 也可
delete[] pStuArr;
三、拷贝构造函数
1.拷贝构造函数能做什么?为什么需要拷贝构造函数?
因为对象的类型多种多样,不像基本数据类型这么简单,所以并不能像普通类型一样直接拷贝,如:
int a=5;
int b=a; //用a的值拷贝给新建的b
因此用一个对象去初始化另一个对象则需要用到拷贝构造函数
2.拷贝构造函数的使用
(1)在Student类中加入如下代码
Student(Student& s) {
this->setName(s.getName());
this->setNum(s.getNum());
}
(2)分析什么情况下会调用拷贝构造函数
- 用一个对象去初始化另一个对象时
- 调用一个函数时作为实参给形参赋值,会调用形参的拷贝构造函数
- 把一个对象作为返回值返回,此时接收该返回值的对象会调用拷贝构造函数
注意:拷贝构造函数与构造函数和析构函数相同,也具有默认的拷贝构造函数
3.浅拷贝与深拷贝
(1)浅拷贝的弊端
前文所述的拷贝构造函数就是一种浅拷贝,当一个对象的成员指针指向的是堆内存时,把该对象用浅拷贝给另一个对象初始化后,此时会出现的结果就是两个对象的不同成员指针指向了同一个堆内存!那么这两个对象析构后,同一个堆内存就会释放两次导致程序崩溃。
#include <iostream>
using namespace std;
class Student {
public:
Student(char *name, int age);
~Student();
private:
char *name;
int age;
};
Student::Student(char *name, int age) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->age = age;
}
//析构函数
Student::~Student() {
delete this->name;
}
int main() {
Student stu("莫莫", 10);
//stu2调用默认拷贝构造通过stu对象来初始化
//只会拷贝资源,不会拷贝空间
Student stu2 = stu;
//程序结束后stu对象会析构,释放一次堆内存
//stu2对象也会析构,再次释放同一个堆内存
return 0;
}
(2)深拷贝
把类中的拷贝构造函数自定义为下列函数即可
Student::Student(const Student &s) {
//让被初始化的对象成员指针指向一个新的堆内存
this->name = new char[strlen(s.name)];
strcpy(this->name, s.name);
}
四、小结
- 用malloc和new初始化类指针的区别是前者不会调用构造函数,后者会调用构造函数
- 但凡使用了堆内存请务必记住释放堆内存
- 拷贝构造函数有3个应用场景:初始化另一个对象、作为函数参数、作返回值
- 深拷贝的应用场景:类的成员指针指向了堆内存,此时要自定义拷贝构造函数