diff --git a/EffectiveC++/Item26.md b/EffectiveC++/Item26.md new file mode 100644 index 0000000..adbf39d --- /dev/null +++ b/EffectiveC++/Item26.md @@ -0,0 +1,73 @@ +## Item 26: 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자 + +생성자와 소멸자를 지니는 변수를 정의하면 반드시 물게 되는 비용이 있다. 프로그램 제어 흐름이 변수의 정의에 닿을 때 생성자가 호출하는 비용와 변수가 유효범위를 벗어날 때 발생하는 소멸자 비용이다. 물론 변수가 사용되지 않은 경우에도 마찬가지다. + +사용하지 않는 변수는 개발과정에서 종종 발생하게 된다. *아마 개발이 기획을 앞서는 상황이나 쓸데없는 최적화, 예상하는 기능등이 여기에 해당할 것 같다.* + +```cpp +std::string encryptPassword(const std::string& password) +{ + using namespace std; + string encrypted; + + if (password.length() < MinimumPasswordLength) + { + throw logic_error("Password is too short"); + } + + // 암호화 알고리즘 + // encrypted을 암호화 + + return encrypted; +} +``` + +이 경우 `encrypted`가 안쓰인다고 보기는 어렵지만, 예외가 발생할 경우 이 변수는 사용되지 않는다. 따라서 변수는 실제로 사용되는 시점에 정의하는 것이 좋다. + +물론 `string encrypted`을 암호화 알고리즘쪽에서 선언하고 사용하는 것도 좋지만, 비용과 보기에 역할을 좀 더 명확하게 설명하기 위해서 변수를 정의함과 동시에 초기화하는 것이 좋다. + +```cpp +{ + std::string encrypted(password); + + encrypt(encrypted); + + return encrypted; +} +``` + +이것이 '늦출 수 있는 데까지'의 진짜 뜻이며 기본적으로 변수를 사용하기 전까지 정의를 늦추는 것은 기본이며, 초기화 인자를 손에 전까지 정의를 늦출 수 있는지도 봐야 한다는 것이다. 이렇게 해야 쓰지도 않을 객체가 만들어졌다 없어지는 일이 생기지 않으며 불필요한 기본 생성자 호출도 일어나지 않는다. 또한 누가 봐도 변수의 의마가 명확한 상태에서 초기화가 일어나기 때문에 문서화에도 도움이 된다. + +### 반복의 경우 + +```cpp +// A 빙법 +Widget w; +for (int i = 0; i < n; ++i) +{ + w = someValue; + ... +} + +// B 방법 +for (int i = 0; i < n; ++i) +{ + Widget w(someValue); + ... +} +``` + +- A 방법: 생성자 1번, 소멸자 1번, 대입n번 +- B 방법: 생성자 n번, 소멸자 n번 + +**클래스 중에는 대입에 들어가는 비용이 생성자-소멸자 쌍보다 적게 나오는 경우가 있는데**, Widget 클래스가 이런 종류에 속한다면 A 방법이 더 효율적일 수 있다. *아마 C++ 11이상의 이동 연산자가 가능하다면 더 효율적일 것이다.* 이 차이는 n이 커질수록 더 커진다. + +반대의 경우에는 B방법이 더 효율적이다. + +A방법의 경우 w라는 이름의 유효범위가 B보다 넓어지게 되는데, 이는 프로그램의 이해도와 유지보수성이 역으로 안 좋아질 수 있다. + +### 정리 + +- 변수 정의는 늦출 수 있을 때까지 늦추자. 프로그램이 더 깔끔해진다. + +반복의 경우에 대부분의 경우에서 A를 사용했는데, 생각해볼만한 내용이 많은 것 같다.