C++的堆与拷贝构造函数

本文深入探讨C++中的内存区域划分,详细对比malloc/free与new/delete的使用及区别,阐述堆对象和堆对象数组的创建与回收,解析拷贝构造函数的作用与实现,区分浅拷贝与深拷贝,特别强调深拷贝在处理堆内存指向的成员变量时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、写在前面

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个应用场景:初始化另一个对象、作为函数参数、作返回值
  • 深拷贝的应用场景:类的成员指针指向了堆内存,此时要自定义拷贝构造函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值