【菜鸟C++学习笔记】18.动态内存

1、内存泄露:

在定义一个指针并初始化后,如果没有删除这个指针就对其重新赋值,就会造成内存泄露,如:

int *p=new int;
p=new int;
第一行时,程序为p定义了一块内存空间,但第二行将新的空间赋给p,此时无法通过p指针访问第一块内存空间了,且第一块内存空间又没被删除,因此会出现内存泄露的情况,为此, 在使用new后,如果不再使用这块内存空间,就要用delete释放它


2、堆中的对象:

堆可以保存变量,当然就可以保存对象。如下面的程序:

#include<iostream>
using namespace std;
class Human
{
public:
	Human()
	{cout<<"构造函数执行中"<<endl;}
private:
	int i;
};
int main()
{
	Human *p=new Human;
	return 0;
}
输出结果:构造函数执行中

分析:在main函数中,定义了一个Human类的指针,在等号右侧创建一块内存空间,同时调用了Human类的默认构造函数构造了一个对象,并将内存空间的地址赋给指针p,其内存空间的大小由Human类对象的成员变量决定。

讨论:

1)如果要删除堆中创建的对象,就直接删除指向该对象的指针即可,这样程序会自动调用对象的析构函数来销毁这个对象并释放内存。

2)如果要访问对象的数据成员和函数,一般用点运算符“.”,即(*p).get(),其中*p表示p指针指向的对象,这样表达方式很别扭,因此C++采用箭头运算符"->"解决这个问题,其格式为p->get(),参考下面程序:

#include<iostream>
using namespace std;
class Human
{
public:
	int get() const{return i;}//const函数不能修改成员变量的值
	void set(int x){i=x;}
private:
	int i;
};
int main()
{
	Human *p=new Human;
	p->set(1);
	cout<<p->get()<<endl;
	delete p;
	return 0;
}
输出结果:1

分析:在public中定义了一个读取数据成员i的函数,和一个设置i的值得set函数,在main函数中,通过定义指针p及箭头运算符访问对象的成员函数。


3、在构造函数中开辟内存空间

可以将类的数据成员定义为指针,然后在构造函数中开辟新空间,并将其地址赋给指针成员,在析构函数中释放指针成员指向的空间,如下面程序:

#include<iostream>
using namespace std;
class A
{
public:
	A();
	~A();
	int get() const{return *i;}//const函数不能修改成员变量的值
	void set(int x){*i=x;}
private:
	int *i;//数据成员定义为指针
};
int main()
{
	A *p=new A;
    cout<<p->get()<<endl;
	p->set(1);
	cout<<p->get()<<endl;
	delete p;
	return 0;
}
A::A()
{
	cout<<"构造函数执行中"<<endl;
	i=new int(99);
}
A::~A()
{
	cout<<"析构函数执行中"<<endl;
	delete i;
}
输出结果:如下图

分析:第11行定义了一个指针成员变量i,第25行在构造函数中将新建的内存空间地址赋给指针变量i,同时将99存到这块空间中,在运行到15行时,调用这个构造函数,通过访问成员函数的方法得到这个*i的值为99,而通过调用set函数重新对i指向的内存上的数值进行修改,再次调用get函数得到修改后的数值,最后删除指针p时调用了析构函数,在析构函数中删除了指针i的空间。

注意:在实际程序中,一个在堆中创建的对象通过成员指针再创建新空间保存数据是没什么意义的,因为在堆中创建对象时已经为它的所有数据成员提供了保存的空间。


4、对象在栈和堆中的区别

存储在栈中的对象,在超出作用域时,会自动调用析构函数来释放这个对象所占用的内存,而存储在堆中的对象则需要程序员自行释放所占用的内存。

例如在上面的程序中,如果在main函数中删掉"delete p;"这条语句,那么在堆中创建的对象将不会被删除,析构函数也不会自动调用,其结果如下图:

而如果将堆中所创建的对象换成在栈中创建对象的话,那么主函数需修改为:

int main()
{
	A a;
    cout<<a.get()<<endl;
	a.set(1);
	cout<<a.get()<<endl;
	return 0;
}

此时输出结果将同第一次一样,系统自动调用析构函数释放内存空间。


5、指针删除时易出现的错误

我们在删除指针指向的内存空间后,应习惯性地把指针赋为空,否则会出现下面的错误:

#include <iostream>
using namespace std;
int main()
{
	int *p=new int;
	*p=1;
	cout<<"p的地址"<<p<<endl;
	delete p;
	cout<<"p的地址"<<p<<endl;
	int *p1=new int;
	cout<<"创建p1后p的地址"<<p<<endl;
	cout<<"p1的地址"<<p1<<endl;
	*p1=10;
	cout<<"*p:"<<*p<<"\t"<<"*p1:"<<*p1<<endl;
	return 0;
}
输出结果:



分析:可以看到,在删除p指向的内存空间后,p保存的地址仍然是原来分配的内存地址,而再开辟新的空间后,系统将把上次删除的内存空间重新开辟出来,地址赋给新的指针p1,因此此时p和p1同时指向同一内存空间,这种错误不易察觉,且容易造成严重后果,因此最好在删除p指针指向的内存空间后,把其值赋值为0。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值