로또
26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자. 본문
다루는 내용
변수 정의를 늦추었을 때의 장점과 변수를 루프의 안과 밖에 선언했을 때 드는 비용과 장단점을 알아보자.
변수 정의 = 생성자 + 소멸자
변수를 다룰 때 반드시 물어야 하는 비용이 있다.
첫 번째는 프로그램 제어 흐름이 변수의 정의에 닿을 때, 생성자가 호출되는 비용이다.
두 번째는 그 변수가 유효범위를 벗어날 때, 소멸자가 호출되는 비용이다.
사용하지 않는 변수
사용하지 않을 변수를 선언하는 사람이 어딨겠냐만은, 주의를 기울이지 않으면 우리가 짠 코드에서도 빈번하게 발생할 것이다. 다음 코드를 보자.
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이 커지면 커질수록 더욱 효율이 좋을 것이다. 그렇지 않다면, 루프 안에 변수를 정의하는 것이 더 좋을 것이다.
또한, 루프 밖에 변수를 정의하는 것은 해당 변수를 볼 수 있는 유효범위가 넓어지므로 프로그램의 이해도와 유지보수성이 나빠질 수도 있다.
각 방법마다 장단점이 있다. 결론적으로 "대입이 생성자 소멸자 쌍보다 비용이 덜 들고", "전체 코드에서 수행 성능에 민감한 부분을 건드리는 중"이라고 생각하지 않는다면, 루프 안에 변수를 정의하자.
이것만은 잊지 말자!
- 변수 정의는 늦출 수 있을 때까지 늦추자. 프로그램이 더 깔끔해지며 효율 또한 좋아진다.
'책 > Effective C++' 카테고리의 다른 글
| 28. 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자. (1) | 2023.11.27 |
|---|---|
| 27. 캐스팅은 절약, 또 절약! 잊지 말자 (3) | 2023.11.25 |
| 25. 예외를 던지지 않는 swap에 대한 지원도 생각해보자. (0) | 2023.11.23 |
| 24. 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자. (1) | 2023.11.21 |
| 23. 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자. (1) | 2023.11.20 |