stack unwinding

김대익·2022년 3월 24일
0
#include <exception>
#include <iostream>

int divide(int a, int b) 
{
	if (b == 0)
    {
    	throw std::runtime_error("divide by 0");
    }
    return a / b;
}

int main() 
{
	try
    {
    	std::cout << divide(10, 0) << std::endl;
    }
    catch (const std::exception &e)
    {
    	std::cout << e.what() << std::endl;
    }
}

프로세스가 실행되면 각 함수에 맞추어 stack이 프로세스에 올라가게 된다
가장 먼저 main()함수가 올라가게 되고

이후 try가 실행되면서 divide()함수가 실행된다

divide()함수 if (b == 0)가 충족되면 내부의 throw std::runtime_error("divide by 0");
exception이 던져지고 exception 객체가 만들어지면서 divide()함수는 프로세스에서 pop이 된다.

그리고 main()함수의 catch구문에서 exception 객체를 가리키게된다

이해를 돕기위해 똑같이 동작하지만 divide()함수를 실행하는 중간과정용 함수 f()를 추가해보면

#include <exception>
#include <iostream>

int divide(int a, int b) 
{
	if (b == 0)
    {
    	throw std::runtime_error("divide by 0");
    }
    return a / b;
}

void f()
{
	std::cout << divide(10, 0) << std::endl;
}

int main() 
{
	try
    {
    	f();
    }
    catch (const std::exception &e)
    {
    	std::cout << e.what() << std::endl;
    }
}

각 함수들이 실행되면서 stack frame을 생성하고


이렇게 exception이 발생했을 때 try catch문이 발견될 때까지
함수들이 pop되는것을 stack unwinding이라고 한다


exception은 overhead가 반드시 생긴다

환경과 코드에 따라 overhead는 다르지만, for문처럼 반복되는 곳에서 사용하면 그만큼 overhead가 늘어나니 반복문에서는 사용하지 않는 것이 좋다.


exception을 사용하면 객체를 생성할 때 주의해야한다

#include <exception>
#include <iostream>

int divide(int a, int b) 
{
	if (b == 0)
    {
    	throw std::runtime_error("divide by 0");
    }
    return a / b;
}

void f()
{
	Cat * cp = new Cat();
	std::cout << divide(10, 0) << std::endl;
    delete cp;
}

int main() 
{
	try
    {
    	f();
    }
    catch (const std::exception &e)
    {
    	std::cout << e.what() << std::endl;
    }
}

위와 같이 객체를 만들고 삭제하는 코드가 있다면

divide()함수가 exception없이 정상 path의 경우

Cat 포인터와 객체 모두 삭제되어 memory leak이 없지만

중간에 exception이 일어나는 경우 exception을 만나자마자 stack unwinding을 하므로

delete cp;가 되지않고 모든 작업이 끝났음에도 Cat객체가 heap에 남아있게된다

이를 방지하고 싶다면 unique pointer를 사용하면 f()함수가 pop될 때 스마트포인터에 대한 destructor가 호출되면서 Cat 객체가 삭제된다

0개의 댓글