목록C++ (43)
방프리
항목 13 : 자원 관리에는 객체가 그만! C++에서는 메모리가 할당되면 당연히 메모리를 해제하는 코드도 넣어주어야 합니다. 기초 중에 기초죠. (여담으로 C#, Java의 경우엔 가비지 컬렉터가 있으니...) 이 항목에서 말하는 핵심 내용은 이 메모리의 할당 및 해제는 객체 자기 스스로가 해야한다는 것 입니다. 스택에 쌓인 메모리는 일정 범위(지역 함수 및 main 함수이겠죠)를 넘어가면 자동으로 해제가 됩니다. 만약 이 자원을 관리하는 주체가 객체 자기 스스로가 아닌 어떠한 함수가 해준다면 어떻게 될까요?? 정말 능력 있는 개발자가 관리한다면 빈틈없이 진행하겠죠 하지만 사람은 언제든지 실수하기 마련입니다. 자신이 메모리를 할당하고서도 해제하는 것을 잊어버릴 수도 있다는 것이죠. 또 다른 경우로는 예외..
항목 12 : 객체의 모든 부분을 빠짐없이 복사하자. 일반적으로 클래스를 생성하면 생성자, 소멸자, 복사 생성자, 복사 대입 연산자 총 4가지의 함수가 기본적으로 생성이 됩니다. 이 중 복사 생성자와 복사 대입 연산자는 합쳐서 복사 함수로 일컫는데 컴파일러가 기본적으로 생성해주지만 기본적으로 생성되는 복사 함수는 정말 문제가 많습니다. 왜일까요? 생성자와 소멸자는 아무 문제가 없는데 유독 복사 함수가 문제인 이유는 클래스는 컴파일러가 아닌 사용자가 제작했기 때문입니다. 그렇기 때문에 어떠한 행동이 맞는지 컴파일러 자신이 판단하기가 어려운 것이죠. 대충 복사 하라고 해서 복사를 했는데 그 과정에서 문제가 생겼다? 그 순간 총체적 난국이 생겨버립니다. 이쯤되면 "어떤 경우에 문제가 생기나요?" 라는 질문이 ..
항목 11 : operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자. 이 항목을 살펴보기 전 자기대입이라는 것이 대해 알아보겠습니다. 자기대입이란? 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 말합니다. int나 일반 데이터 타입의 경우 자체적으로 대입 행동의 정의가 되어 있지만 사용자 정의 class나 구조체의 경우 어떤 행동을 해야할 지 모르기 때문에 정의를 해주어야 합니다. 일반적으로 개발자들이 생각하는 대입 코드를 알아보겠습니다. Widget w, w1; w =w1; 보통의 데이터 타입일 경우 통할 수도 있겠지만 사용자 정의 클래스에서는 어림없는 문법이죠. 무슨 행동을 해야하는 지도 모르고 어떤 데이터를 대입해야할지 아무것도 모릅니다. (컴파일러는 천재가 아닙니다.) 다..
항목 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..