关于栈的底层知识可以看看堆和栈

栈的基础原理

每次在C++中进入一个作用域,其实是在Push栈帧到栈中,而作用域结束后,对应的栈帧则会Push出栈,其中基于栈声明的变量和栈帧中创建的对象都会随之消散。

INFO

栈就像日常生活中,在桌子上一本一本向上堆积的书本,你每次只能从最顶上放置书本或者取走书本,每次取走书本后,会将其丢弃,在书籍中的内容也会随之消失。

作用域是指由{}包裹的代码,往往有类作用域、函数作用域和空作用域等

class Entity
{
public:
	Entity()
	{
		std::cout << "创建对象" << std::endl;
	}
	~Entity()
	{
		std::cout << "销毁对象" << std::endl;
	}
}
 
int main()
{
	{
		Entity e_statck;                 //e_stack在栈中创建
		Entity* e_heap = new Entity();   //e_heap在堆中创建
	}
	//当运行到此时,上面声明的对象e_stack的作用域已经结束,
	//因此在栈中创建的e_stack随着作用域一起结束并销毁
	//而堆中创建的e_heap仍可以继续存活
}

栈对象的使用方法

int* createArray()
{
	//下面代码创建一个整型数组并返回数组指针
	//但是该指针指向的内容会随着函数调用结束而销毁
	//最终得到错误的内容
	int array[50];
	return array;
}
 
void createArray(int* array)
{
	//相关操作
	//避免创建局部变量指针
}
 
int main()
{
	int* a = createArray();
}

在实际代码开发过程中应避免出现,在局部创建变量后返回对应指针的做法,这会导致意料之外的结果。

在代码开发过程中我们可以利用这个特性,帮助我们自动化处理代码。例如作用域指针或者作用域锁

作用域指针

作用域指针是一个类,对于指针的一个包装器,在构造时使用堆分配指针,然后在析构时删除指针,因此我们可以自动化该类的new和delete

class Entity
{
public:
	Entity()
	{
		std::cout << "创建对象" << std::endl;
	}
	~Entity()
	{
		std::cout << "销毁对象" << std::endl;
	}
}
 
//创建了一个最基本的作用域指针
class ScopedPtr
{
private:
	Entity* m_Ptr;
public:
	ScopedPtr(Entity* ptr)
		: m_Ptr(ptr)
	{
	}
	~ScopedPtr()
	{
		delete m_Ptr;
	}
}
 
int main()
{
	{
		//e_heap在堆中创建,需要手动使用delete进行销毁
		Entity* e_heap = new Entity();
		//e_ptr可以自动销毁指针和对象
		ScopedPtr e_ptr = new Entity();
	}
	//超出作用域时,e_ptr作为局部变量会调用析构函数
	//析构时使用delete销毁在堆上创建的entity对象
	//析构结束的同时销毁类中的属性m_ptr指针和e_ptr变量
}

上述的 ScopedPtr 就是智能指针中实现的基础功能,利用这种自动构造,离开作用域时自动析构的特性,可以在许多场景中进行利用。例如制作一个计时器类,在构造时开始计时,在析构时停止计时并输出两个时间差,那么在作用域开始阶段声明后可以得到整个作用域的执行时间,不需要人为的去停止。此外在自动作用域锁方面,可以在函数开头进行声明进行上锁,然后在函数执行完后自动解锁