内存管理面试题

面试题一:设置一个不能被继承的类

思路:
子类继承父类的时候,子类的构造函数会自动调用父类的构造函数;子类的析构函数会自动调用父类的析构函数。所以只要将父类的构造函数和析构函数设置成私有的即可。(一旦设置成私有的,子类继承父类时,就会去调用父类函数和析构函数,从而导致编译器就会报错)

上述方法也存在一定的问题,将父类的构造函数和系统函数设置成私有的,如何实例化对象呢,又如何对不用的对象进行释放呢?

思路:
设置两个接口,分别用来创建对象和释放对象即可。

class A
{
public:
	static A* GetObject()
	{
		return new A();
	}

	void destoryObject()
	{
		delete this;
	}

private:
	A() {};
	~A() {};
};

void test()
{
	A* a = A :: GetObject();
	a-> GetDestory();
}



面试题二:设计一个类,只能在栈上开空间

法1:

思路:
只有使用new运算符,对象才会建立在堆上,所以只要我们禁用new运算符即可。
又知:执行new运算符有两步操作:①调用operator new() 开空间 ②调用构造函数初始化。
所以,只要将operator new() 声明成私有的即可。

class A
{
public:
	A() {};
	~A() {};

private:
	void* operator new(size_t ret) {};
	void operator delete(void* ptr) {};
};

法2:

思路:
上述方法已经提及new运算符的两步操作,所以我们还可以从另一操作下手
因为对象的构造是由构造函数完成的,那么将构造函数设置成保护的即可

但是有一个问题,这样一来,不是就无法在类外创建对象了吗?

思路:
只要在类中定义一个函数,用来返回这个对象即可。
注意:我们只能通过对象调用类的成员函数,将接口声明成静态的,我们就可以在类外调用了。

class B
{
public:
	//提供接口
	static  B GetObject(int b)//静态函数
	{
		return B();
	}

	~B() {};
protected:
	//将构造函数设置为保护
	B(){};

private:
	int _b;
};



面试题三:设计一个类,只能在堆上开空间

方法1:

思路:
只能在堆上开空间,意思说:不能调用类的构造函数来构造对象。
如果简单粗暴地将构造函数声明成私有的,不但无法在类外部调用调用类的构造函数构造对象;也会导致new运算符无法使用,因为调用构造函数是new运算符的操作步骤之一。


那么,我们可以如何设计这个类呢?
提示:
编译器在为类对象分配栈空间时,会先检查析构函数的访问性,其实不止是析构函数,还有其他的非静态函数。如果析构函数无法访问,那么编译器是不会为对象分配栈空间的。


所以,只要我们将析构函数设为私有,类对象就无法建立在栈上了。
因为析构函数声明成私有的,所以需要在类中再设一个接口,来释放内存。

class C
{
public:
	C() {};
	void destory()
	{
		delete this;
	}
private:
	~C() {};
};

方法1还是存在些许问题的,就是如果这个类被继承,析构函数通常被设为virtual
子类会对父类的析构函数进行重写,以实现多态。那如果将父类的析构函数设成私有的,就无法实现继承问题了。

思路:
将类的析构函数和构造函数都设为保护的即可。
再在类中定义两个接口用来创建对象和释放对象,以用来在类外调用。

class D
{
protected:
	D() {};
	~D() {};
public:
	static D* create()
	{
		return new D();
	}
	void destory()
	{
		delete this;
	}
};

void test()
{
D* d = D::create();
	d->destory();
}


面试题四:单例模式

什么是单例模式?

单例模式:一个类只能创建一个对象
单例模式,可以保证类中只有一个实例,并提供一个访问它的全局访问点

单例模式有两种实现模式:

①饿汉模式

饿汉模式:不管你用不用,一上来就给你创建一个唯一的实例化对象出来。
优点:简单。
缺点:如果单例对象构造十分耗时或者占用很多资源;又或者这个对象根本就不会用到。
这种情况下,你一上来就初始化,会导致程序启动时缓慢

//饿汉模式
class Singleton
{
public:
	Singleton* GetInstance()//此处就创建了唯一的对象
	{
		return &instance;
	}

private:
	Singleton() {};//构造函数私有化,外部便无法调用,无法实例化对象

	//c++98写法:
	Singleton(Singleton const& otherinstance);
	Singleton& operator=(Singleton const& otherinstance);

	//c++11写法:
	Singleton(Singleton const& otherinstance) = delete;
	Singleton& operator=(Singleton const& otherinstance) = delete;
	static Singleton instance;
};

Singleton Singleton::instance;//在程序入口之前,就可以完成对单例对象的初始化
//因为这个类是在编译之前创建的,所以可以在main函数之前对其初始化

②懒汉模式

懒汉模式:延时加载,在需要时才创建对象。
缺点:复杂
优点:在需要时才会创建对象,不会白白申请无用对象,也不会造成空间浪费的情况。

//懒汉模式
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		//这里一定要使用双层加锁机制:double-check
		//是因为加锁机制只能使得一个线程new对象,除非解锁
		//但是我这个不为空,就不会new对象了
		if (nullptr == pInstance)
		{
			mutex.lock();//加锁是为了防止多线程并行的情况,防止new出多个对象
			if (nullptr == pInstance)
			{
				pInstance = new Singleton();
			}
			mutex.unlock();
		}
		return pInstance;
	}
	
	//实现一个内嵌的垃圾回收类
	//一般内部类定义的目的,是为了帮外部类做一些事情
	class GetGarbage
	{
	public:
		~GetGarbage()
		{
			if (Singleton::pInstance)
			{
				delete Singleton::pInstance;
			}
		}
	};

	//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
	static GetGarbage Garbage;

private:
	Singleton() {};
	Singleton(Singleton const&) = delete;
	Singleton& operator=(Singleton const&)=delete;

	static Singleton* pInstance;//单例对象指针
	static mutex mutex;//互斥锁
};

Singleton* Singleton::pInstance = nullptr;
Singleton::GetGarbage Garbage;
mutex Singleton::mutex;
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值