C++:内存释放的一些小记录(测试向)

1、函数申请内存未释放

调用函数,函数申请内存不释放

 void fun()
 {
	int* test = new int(0);
	return;
 }
int main() {
	fun();

	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述

调用函数,函数申请内存释放

#include <iostream>
using namespace std;

void fun()
{
  int* test = new int(3);
  delete test;
  return;
 }
int main() {
	{
		fun();
	}
	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述

2、函数申请内存返回指针

调用函数,函数申请内存不释放

#include <iostream>
using namespace std;
int* fun()
{
	int* test = new int[9];
	for (int i=0;i<9;i++)
	{
		test[i] = i;
	}
	return test;
}
int main() {
	{

		int* m1=fun();
		for (int i = 0; i < 9; i++)
		{
			cout << m1[i] << " ";
		}
	}

	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述

调用函数,函数申请内存不释放,在“主函数”释放

#include <iostream>
using namespace std;
int* fun()
{
	int* test = new int[9];
	for (int i=0;i<9;i++)
	{
		test[i] = i;
	}
	return test;
}
int main() {
	{
		int* m1=fun();
		for (int i = 0; i < 9; i++)
		{
			cout << m1[i] << " ";
		}
		delete[] m1;//切勿忘记,需要手动释放,所以这种方法不好
	}

	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、将指针放在类中

指针利用类的构造析构特性来申请内存和释放内存

class MyClass
{
private:
	int* test;
public:
	MyClass(){
		test = new int[9];
	}
	~MyClass(){
		if (test!=nullptr)
		{
			delete[] test;
			test = nullptr;
		}
	}
	int* fun()
	{
		for (int i=0;i<9;i++)
		{
			test[i] = i;
		}
		return test;
	}
};

int main() {
	{
		MyClass s;
		int* m1=s.fun();
		for (int i = 0; i < 9; i++)
		{
			cout << m1[i] << " ";
		}
	}
	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
后续test[i]申请
在这里插入图片描述
在这里插入图片描述
出作用域时调用析构函数~MyClass(){}
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、单例模式

1、饿汉式:即类产生的时候就创建好实例对象,这是一种空间换时间的方式
2、懒汉式:即在需要的时候,才创建对象,这是一种时间换空间的方式

饿汉式

方法一:类内static 对象加类内static函数

首先说一下饿汉式:饿汉式的对象在类产生的时候就创建了,一直到程序结束才释放。即对象的生存周期和程序一样长,因此 该实例对象需要存储在内存的全局数据区,故使用static修饰。

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);

class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() {fout << "单例对象销毁!" << endl; };

	static CSingleton myInstance; // 单例对象在这里!
public:
	static CSingleton* getInstance()
	{
		return &myInstance;
	}
};

CSingleton CSingleton::myInstance;
int main()
{
	CSingleton *ct1 = CSingleton::getInstance();
	CSingleton *ct2 = CSingleton::getInstance();
	CSingleton *ct3 = CSingleton::getInstance();

	return 0;
}

对于饿汉式来说,是线程安全的。运行结果如下所示:
在这里插入图片描述

能够看出,类的实例只有一个,并且正常销毁

方法二:类内static对象指针加类内static函数

错误示范:

如果在类里面的定义改成如下形式:

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);

class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() { fout << "单例对象销毁!" << endl; };

	static CSingleton *myInstance; // 这里改了!
public:
	static CSingleton* getInstance()
	{
		return myInstance; // 这里也改了!
	}
};


using namespace std;
CSingleton * CSingleton::myInstance = new CSingleton();
int main()
{
	CSingleton *ct1 = CSingleton::getInstance();
	CSingleton *ct2 = CSingleton::getInstance();
	CSingleton *ct3 = CSingleton::getInstance();

	return 0;
}

运行结果:
在这里插入图片描述
咦!怎么没有进入析构函数?(类对象出作用域时会调用类的析构函数,类指针出作用域不会调用类的析构函数,友情提示:c++ 析构函数调用时机)这里就有问题了,如果单例模式的类中申请了其他资源,就无法释放,导致内存泄漏!
原因:此时全局数据区中,存储的并不是一个实例对象,而是一个实例对象的指针,即一个地址变量而已!实例对象呢?在堆区,因为是通过new(调用operator new,operator new 先分配内存然后调用构造函数)得来的!(因此new必须手动释放,使用配套的delete(先调用析构函数,然后释放内存))虽然这样能够减小全局数据区的占用,把实例对象这一大坨都放到堆区,可是!如何释放资源呢?

正确示范1

首先能想到的第一个方法:我自己手动释放呀!我在程序结束的时候delete不就可以了?对!这是可以的,程序如下:

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);

class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() { fout << "单例对象销毁!" << endl; };

	static CSingleton *myInstance;
public:
	static CSingleton* getInstance()
	{
		return myInstance;
	}
	static void releaseInstance() // 这里加了个方法
	{
		delete myInstance;
	}
};

CSingleton * CSingleton::myInstance = new CSingleton();
int main()
{
	CSingleton *ct1 = CSingleton::getInstance();
	CSingleton *ct2 = CSingleton::getInstance();
	CSingleton *ct3 = CSingleton::getInstance();
	CSingleton::releaseInstance(); // 手动释放
	return 0;
}

运行结果:
在这里插入图片描述
运行结果没问题!

正确示范2

可是,要是我写着写着我忘记了,没有显式调用释放的函数怎么办?如果有一个自动释放的方法就好了!天无绝人之路,方法二如下

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);

class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() { fout << "单例对象销毁!" << endl; };

	static CSingleton *myInstance;
public:
	static CSingleton* getInstance()
	{
		return myInstance;
	}

private:
	// 定义一个内部类
	class CGarbo {
	public:
		CGarbo() {};
		~CGarbo()
		{
			if (nullptr != myInstance)
			{
				delete myInstance;
				myInstance = nullptr;
			}
		}
	};
	// 定义一个内部类的静态对象
	// 当该对象销毁时,顺带就释放myInstance指向的堆区资源
	static CGarbo m_garbo;
};

CSingleton * CSingleton::myInstance = new CSingleton();
CSingleton::CGarbo CSingleton::m_garbo; // 注意这里!!!
int main()
{
	CSingleton *ct1 = CSingleton::getInstance();
	CSingleton *ct2 = CSingleton::getInstance();
	CSingleton *ct3 = CSingleton::getInstance();

	return 0;
}

运行结果:
在这里插入图片描述

懒汉式

方法一:类内static函数中的static对象

懒汉式单例模式,是在第一次调用getInstance()的时候,才创建实例对象。想到这里,是不是直接把对象定义为static,然后放在getInstance()中。第一次进入该函数,就创建实例对象,然后一直到程序结束,释放该对象。代码如下:

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);

class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() { fout << "单例对象销毁!" << endl; };

public:
	static CSingleton * getInstance()
	{
		static CSingleton myInstance;
		return &myInstance;
	}

};

int main()
{
	CSingleton *   ct1 = CSingleton::getInstance();
	CSingleton *   ct2 = CSingleton::getInstance();
	CSingleton *   ct3 = CSingleton::getInstance();

	return 0;
}

运行结果:
在这里插入图片描述
程序正常运行。

方法二:类内static函数加类内static对象指针加类中类static对象

此时,如果想把对象放在堆区,也可以这么实现:

#include<iostream>
#include <fstream>
using namespace std;

fstream fout("destructor.txt", ios::app);


class CSingleton
{
private:
	CSingleton() { fout << "单例对象创建!" << endl; };
	CSingleton(const CSingleton &);
	CSingleton& operator=(const CSingleton &);
	~CSingleton() { fout << "单例对象销毁!" << endl; };

	static CSingleton *myInstance;


public:
	static CSingleton * getInstance()
	{
		if (nullptr == myInstance)
		{
			myInstance = new CSingleton();
		}
		return myInstance;
	}

private:
	// 定义一个内部类
	class CGarbo {
	public:
		CGarbo() {};
		~CGarbo()
		{
			if (nullptr != myInstance)
			{
				delete myInstance;
				myInstance = nullptr;
			}
		}
	};
	// 定义一个内部类的静态对象
	// 当该对象销毁时,顺带就释放myInstance指向的堆区资源
	static CGarbo m_garbo;
};

CSingleton * CSingleton::myInstance = nullptr;
CSingleton::CGarbo CSingleton::m_garbo;
int main()
{
	CSingleton *   ct1 = CSingleton::getInstance();
	CSingleton *   ct2 = CSingleton::getInstance();
	CSingleton *   ct3 = CSingleton::getInstance();

	return 0;
}

运行结果:
在这里插入图片描述
对于懒汉式这两种情况,当调用getInstance()函数时,如果对象还没产生(第一种状态),就需要产生对象,然后返回对象指针。如果对象已经存在了(第二种状态),就直接返回对象指针。

多线程

当单线程时,没有问题。但是,多线程情况下,如果一个函数中不同状态有不同操作,就要考虑线程同步的问题了。因此,我们需要修改一下getInstance中的实现。
举例如下:

第一种懒汉式:

static CSingleton * getInstance()
{	
	lock();
	static CSingleton myInstance;
	unlock();
	return &myInstance;
}

第二种懒汉式:

static CSingleton * getInstance()
	{	
		if (nullptr == myInstance)
		{
			lock();// 需要自己采用适当的互斥方式
			if (nullptr == myInstance)
			{
				myInstance = new CSingleton();
			}
			unlock();
		}
		return myInstance;
	}

注意!由于线程和使用的操作系统有关,因此这里的lock()和unlock()函数仅作说明示意,并未实现。都是常见的线程同步方法,可以查询其他资料来实现。这里不再赘述。

总结

多线程目前还没学习,所以此部分代码并未上机,以上纯属个人笔记,总结若有错误,欢迎指出!

参考文献:
C++实现单例模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值