본문 바로가기

전체 글

(139)
[C++] 13. 자원 관리에는 객체가 그만! 자원 관리C++에서 말하는 자원 관리는 대부분 메모리 누수를 방지하기 위한 방법에 대해서 이야기한다고 생각한다. 당연히 이번에도 그러한 내용을 다룬다.class Investment { ... };void fun(){ Investment *pInv = createInvestment(); // 팩토리 함수 호출 ... delete pInv;} 해당 코드를 보게 되면 문제없이 pInv를 메모리에서 해제하는 것처럼 보이지만, 만약 … 부분, 즉 중간 부분에 루프문이나 예외문과 같은 코드를 작성하여 도달하지 못하게 된다면 메모리 누수가 발생하게 된다.스마트 포인터(smart pointer): auto_ptr소프트웨어 개발에 쓰이는 상당수의 자원이 힙에서 동적으로 할당되고, 하나의 블록(block)..
[C++] 12. 객체의 모든 부분을 빠짐없이 복사하자 부분 복사(partial copy) 원인class Customer {public: ... Customer(const Customer& rhs); Customer& operator=(const Customer& rhs); ...private: std::string name; Data lastTransaction; // 새로운 변수};Customer::Customer(const Customer& rhs) : name(rhs.name) { ... }Customer& Customer::operator=(const Customer& rhs){ name = rhs.name; return *this;} 위의 코드를 보면 새로운 변수를 추가했을 때 name이 복사되지 않은 것..
[C++] 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 자기대입자기대입(self assignment)이란, 어떤 객체가 자기 자신에 대한 대입 연산자를 적용하는 것을 말한다. w = w;a[i] = a[j];*px = *pv; 위 코드처럼 명확하게 자기 대입을 하는 경우는 드물지만, 의도치 않게 자기 대입이 발생할 수도 있다. 이러한 코드의 문제점은 자원을 사용하기 전에 해제해 버릴 수도 있다는 점이다. class Bitmap { ... };class Widget{ ...private: Bitmap *pd;}; 예를 하나 들자면, 동적 할당된 비트맵을 가리키는 원시 포인터를 데이터 멤버로 갖는 클래스를 하나 만들었다고 가정한다면Widget& Widget::operator=(const Widget& rhs){ delete pb; pb = ..
[C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 int x, y, z;x = y = z = 15; // x = (y = (z = 15));와 같음 C++에서 대입 연산은 여러 개가 사슬처럼 엮일 수 있는 흥미로운 성질을 가진다. 또 하나의 재미있는 특성은 대입 연산이 우측 연관(right-associative) 연산이라는 점이다. 예를 들어 x = (y = (z = 15));와 같이 대입 연산이 사슬처럼 엮이려면, 대입 연산자가 좌변 인자에 대한 참조자를 반환하도록 구현되어 있어야 한다. 이러한 구현 방식은 일종의 관례(convention)로, 이러한 클래스를 작성할 때 대입 연산자가 포함된다면 이 관례를 따르는 것이 좋다. = 연산자가 포함된 +=, -=, *= 등의 연산자에도 동일한 규약이 적용되어야 하며, 대입 연산자의 매개변수 타입이 일반적이지..
[C++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 class Transaction {public: Transaction(); virtual void logTransaction() const = 0; ...};Transaction::Transaction(){ ... logTransaction();}class BuyTransaction: public Transaction {public: virtual void logTransaction() const; ...};class SellTransaction: public Transaction {public: virtual void logTransaction() const; ...};BuyTransaction b; 해당 코드를 보면 BuyTransaction의 생성자에..
[C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 class DBConn {public: ... ~DBConn() { db.close(); }private: DBConnection db;}; 위 코드를 보면 문제 될 만한 부분은 없어 보인다. 다만 db.close()에서 예외가 발생한다면 어떻게 해야 할지 고민할 필요가 있다. 즉, 예외를 던질 수 있고 실패할 수 있는 코드를 소멸자에 넣어야 한다면 어떻게 해야 하는가에 대한 이야기다. 대개 두 가지 방법이 있다. 예외가 발생하면 프로그램을 바로 종료해 버리거나, 혹은 호출한 곳에서 발생한 예외를 삼켜 버리는 것이다. 하지만 두 방법 모두 문제점이 존재하므로 좋은 선택이라 보기 어렵다. 어느 쪽이든 중요한 것은 예외가 발생한 원인에 대해 프로그램이 어떤 조치를 취할..
[C++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 가상 소멸자 필요성실로 어처구니없는 재앙을 12개월 할부로 앞당겨 구매하는 방법이라 할 수 있겠습니다.class TimeKeeper {public: TimeKeeper(); ~TimeKeeper(); ...};class AtomicClock: public TimeKeeper { ... };class WaterClock: public TimeKeeper { ... };class WristWatch: public TimeKeeper { ... };TimeKeeper *ptk = getTimeKeeper();delete ptk; 팩토리 함수를 통해 반환되는 값이 AtomicClock이라고 가정했을 때, C++의 규정에 따르면 기본 클래스 포인터를 통해 파생 클래스 객체가 삭제될 때, 그 기본 ..
[C++] 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자 “5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자”의 연장선에서, 사용하지 않는 함수를 처리하는 방법으로 public이 아닌 private으로 선언하여 해결하는 방법을 제시한다. 다만, 이 방법은 friend 함수가 호출할 가능성이 있으므로, 정의하지 않고 선언만 하는 방식으로 해결한다.class HomeForSale {public: ...private: ... HomeForSale(const HomeForSale&) HomeForSale& operator=(const HomeFOrSale&);}; 다만 이러한 해결 방법에 아쉬운 점은 링크 시점 에러로 들어가기에 이를 컴파일 시점으로 옮기게 하는것이 좋다. 이를 위해 위의 방법을 클래스 자체에 넣지말고 별도의 클..