3. 생성자와 비 멤버 함수를 가상함수처럼 동작하게 만들기
가상 생성자
자신이 받은 입력 데이터에 의존하여 다른 타입의 객체를 생성하는 함수
ex ) NLComponent를 상속한 TextBlock, Graphic 클래스가 있을 때, 이를 사용하는 NewLetter 클래
스에서 아래와 같이..
static NLCompnent * readComponent(istream& str);
NewLetter::NewLetter(istream& str) {
components.push_back(readComponent(str));
}
readComponent의 str 내용에 따라 Textblock을 생성할지 Graphic를 생성할 지 결정.
가상 복사 생성자
NLComponent에 clone() 같은 가상함수를 만들고 상속받은 클래스에서 return type만 달리하는 동
일한 함수 clone() { return new TextBlock(*this) } 혹은 new Graphic(*this))을 구현
비멤버함수를 가상함수처럼..
원하는 일을 하는 가상함수를 만들고 비 가상함수에서 이를 호출
4. 클래스 인스턴스의 개수를 의도대로 제한하는 방법
객체를 전혀 생성하지 않거나 하나만 만들기
1. 생성자를 private으로 한다.
2. static으로 선언된 자신의 객체를 갖는다.
3. 비멤버 friend 호출 함수를 통해 위에서 만든 객체를 불러온다.
* 정적 객체가 클래스 안에 있으면 사용 여부와 상관없이 생성, 함수에 있으면 호출시점에 생성.
* 비 멤버함수의 inline은 내부 연결을 가지고 이런 함수는 목적 코드에서 중복 생성될 수 있다.
객체 생성이 이루어지는 세 가지 상황
numObject 등의 내부 변수를 통해서 객체의 수를 관리할 수도 있음.
생성자에서 ++하고 소멸자에서 -- 하는 방식으로.
하지만 이럴 경우 1)자신의 객체를 생성할 때, 2)클래스를 상속받거나 3)다른 클래스의 멤버로(합성)
존재할 때도 numObject가 증가하게 됨.
이런 일을 방지하기 위해
1. 생성자는 private으로 만들자
2. 유사생성자(static 함수로 자기 클래스의 포인터를 반환하는 함수)를 사용하자!
3. 이제 생성자에서 카운팅을 하자. (정해진 개수가 넘으면 nullptr를 반환하는 식으로 예외처리)
4. numObject는 static 멤버로 만들자(static멤버는 반드시 ‘정의’해야 함)
5. 클래스 인스턴스의 개수를 의도대로 제한하는 방법
인스턴스 카운팅 기능을 가진 기본 클래스
객체 수를 카운팅하는 클래스가 추가로 필요하다면 위의 작업을 모두 반복해야 한다.
이런 귀찮음은 ‘클래스 템플릿’을 통해 해결하자.
object counting 하는 함수, 생성자, 소멸자 등을 템플릿으로 만들고
사용해야할 클래스에서 이를 상속받는다.
• public으로 상속받을 경우 가상 소멸자를 사용
• private상속 시 상속받은 클래스의 public영역에 using Counted<Printer>::objectCount 식으로 선
언하면 public처럼 쓸 수 있다.
6. 힙에만 생성되거나 힙에는 만들어지지 않는 클래스
객체가 Heap에만 생성되게 하기
생성자는 public 소멸자는 private 에 만들기(생성자는 좀 많으니까, 소멸자만)
이 클래스를 상속할 땐? 소멸자를 protected로 만들어 해결,
객체를 다른 클래스의 멤버로 넣을 땐? 객체의 포인터를 넣는걸로 해결
어떤 객체가 Heap에 생성되었는지 그렇지 않은지를 알아내는 방법
왜? delete this를 하고 싶은데 이게 heap에 생성된 객체인지 아닌지를 모르니까..
하지만 그런 방법은 없다!!
flag를 만들어 new할 때 true, delete할 때 false하는 방법? new []할 때 문제
지역변수를 만들고 객체와 주소를 비교하면? data영역에 저장될 경우 문제
+ heap, stack 영역의 주소가 시스템마다 다르므로 이식성 떨어짐
이 객체가 어느 영역에 생성되었는지를 아는 것보다 포인터가 삭제해도 되는 것인지 판단이 더 쉬
움
주소 콜렉션을 생성한다!!
주소 콜렉트를 하는 mixin 추상클래스를 만들고 (딱히 지정할 순수 가상 함수가 없을 땐 소멸자를!!)
이놈을 상속받아서 사용하자.
* mixin 클래스 : 명확한 기능 하나만을 제공하는 클래스, 파생클래스의 다른 기능과 호환되도록 만
듦
7. 힙에만 생성되거나 힙에는 만들어지지 않는 클래스
객체가 힙에 생성되지 않게 하기
간단하다!!
operator new, delete, new[], delete[]를 private으로 만들자
파생 클래스에서는? public으로 재 정의하지 않는 한 자연스럽게 해결된다.
다른 클래스의 멤버로 쓰일 때 어떤 클래스가 new한다 해도 그 멤버들까지 new하는 건 아니다. 문
제X
8. 스마트 포인터
리소스 관리, 반복 코딩의 자동화!! But 동작은 벙어리 포인터처럼 하자
생성 대입 소멸
• 생성시 가리키는 대상은 nullptr로 하자.
• auto_ptr은 복사 시 소유권이 이전된다. 따라서 스마트포인터를 인자로 넘길 때 값복사 하지 말
자.
역참조 연산자 구현
• operator* 로 실제 가리키는 포인터를 반환하자
• operator->도 마찬가지로 pointee를 가져오게 하자.(근데 std::shared_ptr에서는 get하던데..)
스마트 포인터가 nullptr인지 점검
• operator! 을 오버로딩해서 if (!ptn) 식으로..
• if(!pa == !po) 같은 구문은 대응 못함
9. 스마트 포인터
스마트 포인터를 벙어리 포인터로 변환
• 무슨 일이 있어도 operator() (암시적 변환) 를 지원하지 말자 – 지 멋대로 동작한다.
스마트 포인터와 상속 기반의 타입변환
• 스마트 포인터 객체는 설령 pointee들끼리 상속관계에 있다 해도 그 관계를 알 방법이 없다. 이
때는 암시적 변환 연산자를 위한 템플릿 함수(멤버 함수 템플릿)를 제공한다. 클래스 내부에서
template<class newType>
operator SmartPtr<newType>() { return SmartPtr<newType>(pointee); }
• 함수에서 부모 클래스의 smart pointer를 인자로 받도록 정의되어 있을 때, 자식 클래스의 객체
를 인자로 넘기면 컴파일러 내부에서는
1. 우선 부모 SP 클래스에서 자식클래스 SP 포인터를 취하는 ‘단일 인자 생성자’를 찾는다.
2. 자식 SP 클래스에서 부모 SP클래스로의 ‘암시적 타입 연산자’를 찾는다.
3. 2와 같은 일을 하는 ‘멤버 함수 템플릿’을 찾는다.
10. 스마트 포인터
스마트 포인터를 벙어리 포인터로 변환
• 무슨 일이 있어도 operator() (암시적 변환) 를 지원하지 말자 – 지 멋대로 동작한다.
스마트 포인터와 상속 기반의 타입변환
• 스마트 포인터 객체는 설령 pointee들끼리 상속관계에 있다 해도 그 관계를 알 방법이 없다. 이
때는 암시적 변환 연산자를 위한 템플릿 함수(멤버 함수 템플릿)를 제공한다. 클래스 내부에서
template<class newType>
operator SmartPtr<newType>() { return SmartPtr<newType>(pointee); }
• 함수에서 부모 클래스의 smart pointer를 인자로 받도록 정의되어 있을 때, 자식 클래스의 객체
를 인자로 넘기면 컴파일러 내부에서는
1. 우선 부모 SP 클래스에서 자식클래스 SP 포인터를 취하는 ‘단일 인자 생성자’를 찾는다.
2. 자식 SP 클래스에서 부모 SP클래스로의 ‘암시적 타입 연산자’를 찾는다.
3. 2와 같은 일을 하는 ‘멤버 함수 템플릿’을 찾는다.
우리는 그냥.. std::shared_ptr을 갖다 쓰자!!