Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

로또

26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자. 본문

책/Effective C++

26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자.

아롱로또 2023. 11. 24. 18:15

다루는 내용

변수 정의를 늦추었을 때의 장점과 변수를 루프의 안과 밖에 선언했을 때 드는 비용과 장단점을 알아보자.

 

변수 정의 = 생성자 + 소멸자

변수를 다룰 때 반드시 물어야 하는 비용이 있다.

첫 번째는 프로그램 제어 흐름이 변수의 정의에 닿을 때, 생성자가 호출되는 비용이다.

두 번째는 그 변수가 유효범위를 벗어날 때, 소멸자가 호출되는 비용이다.

사용하지 않는 변수

사용하지 않을 변수를 선언하는 사람이 어딨겠냐만은, 주의를 기울이지 않으면 우리가 짠 코드에서도 빈번하게 발생할 것이다. 다음 코드를 보자.

std::string EncryptPassword(string& password){
   using namespace std;
   string encrypted;
   if(password.length() < minimum_password_length){
      throw logic_error("Password is too short");
   }
   encrypted = password;
   encrypt(encrypted); // 인자를 암호화하는 함수
   return encrypted;
}

위 코드는 encrypted 변수를 선언한 후, password의 길이를 검사하고 있다. 만약 password의 길이가 너무 짧다면, 예외를 던지며 함수를 종료한다. 이 때, encrypted 변수는 생성되었음에도 사용되지 않은 것이다.

 

이러한 모습을 확인했으니, encrypted 변수를 정의하는 일은 꼭 필요해지기 전까지 미루자.

std::string EncryptPassword(string& password){
   using namespace std;
   if(password.length() < minimum_password_length){
      throw logic_error("Password is too short");
   }
   string encrypted;
   encrypted = password;
   encrypt(encrypted); // 인자를 암호화하는 함수
   return encrypted;
}

위 코드는 이전보다 괜찮아보인다. 하지만 여전히 무언가 찝찝하다.

이전에 "4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자." 에서 객체를 기본 생성한 후 원하는 값을 대입하는 방법보다 원하는 값으로 직접 초기화하는 방법이 더 좋음을 이야기했었다. 기본 생성자 호출 후 복사 대입 연산자를 호출하는 것보다 복사 생성자 한 번을 호출하는 것이 더 효율적이기 때문이다.

 

그러니 다음과 같이 한 번 더 수정하자.

std::string EncryptPassword(string& password){
   using namespace std;
   if(password.length() < minimum_password_length){
      throw logic_error("Password is too short");
   }
   string encrypted(password);
   encrypt(encrypted); // 인자를 암호화하는 함수
   return encrypted;
}

성공적으로 변수 정의를 늦출 수 있을 때까지 늦춘 코드를 완성하였다.

 

루프 밖에 변수 정의하기 vs 루프 안에 변수 정의하기

누구나 한 번쯤 해보았을 고민이다. 다음은 각각 루프의 밖과 안에 변수를 정의하는 코드의 예시이다.

// 루프 밖에 정의
Widget w;
for(int i=0; i<n; i++){
   w = // i에 따라 달라지는 값
   ...
}

// 루프 안에 정의
for(int i=0; i<n; i++){
   Widget w = // i에 따라 달라지는 값
   ...
}

코드를 통해 알 수 있듯이, 루프 밖에 변수를 정의하는 것은 생성자 1번, 소멸자 1번, 대입 n번의 비용을 소모한다

반면에, 루프 안에 변수를 정의하는 것은 생성자 n번, 소멸자 n번의 비용을 소모한다.

 

클래스 중에는 대입에 들어가는 비용이 생성자-소멸자 쌍보다 적게 나오는 경우가 있는데, 만약 Widget 클래스가 그렇다면 루프 밖에 변수를 정의하는 것이 n이 커지면 커질수록 더욱 효율이 좋을 것이다. 그렇지 않다면, 루프 안에 변수를 정의하는 것이 더 좋을 것이다.

 

또한, 루프 밖에 변수를 정의하는 것은 해당 변수를 볼 수 있는 유효범위가 넓어지므로 프로그램의 이해도와 유지보수성이 나빠질 수도 있다. 

 

각 방법마다 장단점이 있다. 결론적으로 "대입이 생성자 소멸자 쌍보다 비용이 덜 들고", "전체 코드에서 수행 성능에 민감한 부분을 건드리는 중"이라고 생각하지 않는다면, 루프 안에 변수를 정의하자.

 

이것만은 잊지 말자!

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