728x90
반응형

생성자 혹은 소멸자를 끌고 다니는 타입으로 변수를 정의하면 반드시 물게 되는 비용이 두 개 있다. 

프로그램 제어 흐름이 변수의 정의에 닿을 때 생성자가 호출되는 비용, 변수가 유효 범위를 벗어날 때 소멸자가 호출되는 비용이다.

 

이러한 비용은 변수가 정의만 돼도 부과된다. 예제를 보자.

std::string encryptPassword(const std::string& password)
{
    using namespace std;
    string encrypted;
    if(password.length() < MinimumPasswordLength)
    {
        throw logic_error("Password is too short");
    }
    return encrypted;
}

encrypted를 안 쓴다고 할 순 없지만, 예외가 발생되면 이 변수는 사용되지 않지만 encrypted 객체의 생성과 소멸에 대한 비용은 부과된다.

이를 해결하기 위해 encrypted의 위치를 옮겨보자.

std::string encryptPassword(const std::string& password)
{
    using namespace std;
    if(password.length() < MinimumPasswordLength)
    {
        throw logic_error("Password is too short");
    }
    
    string encrypted;
    encrypted = password;
    
    // 비밀번호 암호화
    encrypt(encrypted);
    
    return encrypted;
}

위와 같이 만들면 예외가 발생할 때는 사용하지 않게 되면서 생성자와 소멸자에 대한 자원낭비는 사라지지만 encrypted 변수가 생성될 때 기본 생성자가 호출되고, password를 대입하게 되므로 생성할 때 초기화하는 것보다 효율이 떨어진다.

 

즉, 아래처럼 사용하는 것이 정의와 동시에 복사 생성자를 통해 초기화 함으로써 성능을 높일 수 있고 프로그램도 깔끔해 짐을 알 수 있다.

std::string encryptPassword(const std::string& password)
{
  if (password.length() < MinimumPasswordLength)
  {
    throw logic_error("Password is too short");
  }

  using namespace std;

  string encrypted(password);// 정의와 동시에 초기화. 복사 생성자가 사용된다.

  // 비밀번호 암호화
  encrypt(encrypted);

  return encrypted;
}

 

루프 상황에서의 변수 정의

루프 바깥쪽에 정의

Widget w;
for (int i = 0; i < n; ++i)
{
  w = i에 따라 달라지는 값;
}

비용은 생성자 1번, 소멸자 1번, 대입 n번이다.

 

루프 안쪽에 정의

for (int i = 0; i < n; ++i)
{
  Widget w(i에 따라 달라지는 값);
}

비용은 생성자 n번, 소멸자 n번이다.

 

결과를 보면, 일반적인 경우에는 바깥쪽에 정의하는 것보다 안쪽에 정의하는 것이 변수를 볼 수 있는 유효 범위가 좁아지기 때문에 프로그램의 이해도와 유지보수성이 증가하여 안쪽에 정의하는 것이 좋다.

하지만 대입에 들어가는 비용이 생성자-소멸자 쌍보다 적게 나오고 이러한 성능 하나하나를 민감하게 따져야 하는 경우라면 바깥쪽에 정의하는 것도 고려해볼 만하다.

 

요약

  • 변수 정의는 늦출 수 있을 때까지 늦추자. 프로그램이 더 깔끔해지며 효율도 좋아진다.
728x90
반응형