목록C++/Effective C++ (32)
방프리
항목 10: 대입 연산자는 *this의 참조자를 반환하게 하자 C++에서 기본적으로 대입 연산을 우측 연관 연산이라는 것을 이용하여 진행합니다. 예를 들어 x = y = z = 15; 라는 문장이 있다면 컴파일러는 x = (y = (z = 15) ) ); 이렇게 해석하게 되죠 대신 반환하는 것은 좌변 인자에 대한 참조자를 반환하도록 구현되어야 합니다. 일종의 관례 같은 것이죠 간단한 예를 통해 알아보겠습니다. class Point { private: int x, y; public : Point(); ~Point(); Point& operator= (Point p) { x = p.x; y = p.y; return *this; } }; 이런 식으로 자기 자신을 반환하도록 합니다. 이번 항목은 꼭 외우고 넘어..
항목 9: 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. 객체 지향에 들어서면서 가장 중요해진 것이 함수 호출의 순서입니다. C언어의 경우 특징이 절차지향이기 때문에 순서대로 가기에 상관이 없었지만 C++에 들어와서는 이야기가 달라졌죠 이번 항목도 앞서 말한 것과 관련이 깊은 내용이라고 생각합니다. 간단하게 예를 들어보겠습니다. class Person // 사람 관련 클래스 { public: Person(); virtual void Action() const = 0; // 행동에 관한 함수 }; class Student : public Person { public: Student() { Action(); // 객체 생성자 안에서 가상 함수를 호출합니다. } }; 자 이 예제에서 St..
항목 8 : 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 - 데이터를 삭제하거나 지울 때 그 과정이 부드럽게 진행이 된다면 그만큼 더할나위 좋을 것이 없습니다. 하지만 현실은 그러지 못하죠. 분명히 잘 동작하다가도 마지막에 소멸자에서 삭제가 진행되면서 오류가 발생할 수도 있죠 이 때 이 오류에 대한 예외처리는 소멸자에서 발생해서는 안됩니다. 소멸자에서 처리는 가능하지만 그다지 좋은 코드도 아닐 뿐더러 오류에 대한 대책이 마땅히 있는 것도 아니지요 그렇기 때문에 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다는 말을 프로그래머들은 항상 새기고 있어야 합니다. 다음의 예제 코드를 통해 구현 방식에 대해 숙지하면 될 것 같습니다. class DBConn //DB를 연결하는 클래스 { public : ......
항목 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. 이 주제를 시작하기에 앞서 다형성이란 무엇인지 먼저 살펴보겠습니다. 다형성이란? 객체지향(OOP) 다섯 가지 중 하나로 정의는 동일한 조작방법으로 동작시키지만 동작 방법은 다른 것이라고 할 수 있습니다. 즉, 조작 인터페이스를 제공하는 부모 클래스를 만든 후 이 부모 클래스를 상속받는 자식 클래스를 통해 어떻게 조작할 것인지 정의해주는 것이죠 정말 간편합니다. 부모클래스에 있는 하나의 메소드를 통해 자식클래스마다 각기 다른 행동을 나타낼 수 있기 때문이죠 하지만 이 편리한 기능에는 복병이 숨어있습니다. 바로! 자식클래스로 생성된 객체는 반드시 부모 클래스의 소멸자를 통해 마지막으로 소멸되기 때문입니다. 객체가 생성되고 소..
항목 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자! 클래스를 선언할 시 Default로 생성되는 함수 네 가지가 있다. 생성자, 소멸자, 복사 생성자, 복사 대입 연산자이다. 이 네 함수는 클래스가 생성되면 무조건 생성되는 함수들이다. 즉, 사용자가 함수를 만들지 않아도 기본적으로 생성된다는 뜻이다. 정말 좋지 않은가? 자동으로 알아서 만들어 주니 말이다. 하지만 앞날을 생각해본다면 절대로 좋은 현상이 아닐 뿐더러 오류의 발생의 여지가 충분히 있다. 예제를 한번 들여다보자 template class cNameObject { public : cNameObject(const char *name, const T& value); cNameObject(const std::string& n..
항목 4: 객체를 사용하기 전에 반드시 그 객체를 초기화 하자!! 객체(또는 변수)를 선언 시 초기화를 하지 않을 경우 알 수 없는 곳에서 터지기 마련이다. 이를 방지하려면 항상 객체를 선언 시 초기화 하는 것을 잊지 말아야 한다. 특히 class의 경우 생성자 부분에서 대입으로 하는 것이 아닌 초기화 리스트를 사용하여 반드시 초기화를 시켜주어야 한다. (초기화 리스트를 작성 시 멤버 변수 선언 순으로 초기화를 해준다면 당신은 굿 코더) 만약 멤버변수의 양이 너무 많아 초기화 리스트가 보기 싫다면 대입으로도 오류를 막을 수 있는 변수를 따로 모아 Default값으로 넣어주어도 괜찮다. But, 주의해야할 사항 한 가지 바로 비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다는 사실이다. 책의 ..
항목 3: 낌새만 보이면 const를 들이대 보자! const란? 정말 좋은 키워드 왜냐하면 '의미적 제약'을 소스 코드에서 붙일 수 있고 이 원칙을 컴파일러는 항상 지켜준다. 만약 어떠한 변수의 데이터가 절대 변하지 않는 수라면 무조건!! const를 붙여주는 것이 맞다. 예전에 c++강의 수업을 들었을 때 정말 훌륭한 c++개발자는 const를 잘 활용하는 개발자라고 하는데 이는 틀린 말이 아니다. 여기서 중요한 점은 대개 개발자들 중 const의 위치를 두고 어떠한 것을 쓰는지는 천차만별이기 때문에 밑의 예를 눈에 익히는 것이 좋다. 1. void f1 ( const Widget *pw); //f1은 상수 Widget 객체에 대한 포인터를 매개변수로 취함 2. void f2 ( Widget cons..
항목 1 : C++를 언어들의 연합체로 바라보는 안목은 필수 *C++를 사용한 효과적인 프로그래밍 규칙은 경우에 따라 달라진다. 그 경우란, C++의 어떤 부분을 사용하느냐이다. 항목 2 : #define을 쓰려거든 const, enum, inline을 떠올리자 제목에서도 알 수 있듯이 #define 키워드보다 const, enum이나 혹은 inline을 좀 더 활용하자는 것이다. 이유인 즉 컴파일 시점에서 오류 찾기, 또는 메모리 할당 등 여러 이유 때문이다. 다음 예제를 보면서 알아보자 ex) #define EPSILONE 0.0001f 만약 이 define 구문이 컴파일러 입장에서는 어떤 식으로 보일까? 컴파일 시점에서 define 키워드가 자체적으로 EPSILONE 이라는 문자열을 밀어버리고 그 ..