[More Effective C++] 항목 3 ~ 4
항목 3 : 배열과 다형성은 같은 수준으로 놓고 볼 것이 아니다.
- 상속성이 주는 혜택 : 다형성
- 파생 클래스 객체의 배열을 기본 클래스 포인터/참조자를 통해 조작
- 실제 동작이 바라던 대로가 아니다!
- 기본 클래스 배열이 들어갈 자리에 파생 클래스가 들어가면 발생하는 문제
- 파생 클래스의 크기를 기본 클래스의 크기로 가정하므로 미정의 결과가 나타난다.
- 기본 클래스의 소멸자를 호출하므로 파생 클래스가 온전히 삭제되지 않는다.(미졍의 결과)
- 다형성 / 포인터 산술 연산은 간단하지 않다.
- 배열 연산에는 거의 항상 포인터 산술 연산이 따라다닌다.
-
즉, 배열과 다형성은 물과 기름의 관계이다.
- 구체 클래스를 상속하여 다른 구체 클래스를 만들지 않게끔 설계하면 실수를 저하시킨다.(33)
항목 4 : 쓸데 없는 기본 생성자는 그냥 두지 말자
-
기본 생성자 : 아무런 인자도 받지 않고 호출될 수 있는 생성자
- 기본 생성자가 없는 클래스 사용상의 주의사항
- 배열을 생성할 때 : 일반적으로 배열의 요소 객체에 생성자 매개변수를 지정할 수 없다.
- 해결법
- 배열을 힙에 만들지 않을 때는 배열이 정의된 위치에서 매개변수를 직접 삽입한다.
- ex)
EquipPiece bestPieces[] = { EquipPiece(ID1), EquipPiece(ID2), ... };
- 포인터의 배열 사용
- ex)
cpp EquipPiece* bestPieces[10]; EquipPiece** bestPieces = new EquipPiece*[10]; for(int i = 0 ; i < 10 ; i++) bestPieces[i] = new EquipPiece(IDNum);
- 배열 내 포인터의 객체 삭제를 잊으면 안된다. »+ 필요 메모리 사용량이 증가한다.(객체 메모리 + 포인터 공간(추가 필요)) -> 비가공 메모리 할당 후 메모리 지정 new로 그 메모리 안에 객체를 생성하게 하면 메모리 공간을 줄일 수 있다.(8)
- 대부분의 프로그래머가 이것과 친하지 않음.
- 객체 삭제 시 소멸자를 손으로 직접 호출해야 한다.
- operator delete[]를 호출해 원래의 비가공 메모리를 직접 해제해야 한다.
- 해결법
- 많은 템플릿 기반 컨테이너 클래스에 먹일 수가 없다.
- 컨테이너 클래스를 인스턴스화 하기 위해서는 템플릿 매개변수로 들어가는 타입이 기본 생성자를 가지고 있어야 하기 때문이다.(클래스 / 구조체 등의 복합 타입 외에 int, double 같은 원시 타입도 기본 생성자를 가지고 있다.)
- 가상 기본 클래스에 기본 생성자가 없으면 개발에 쓰기 어렵다.
- 가상 기본 클래스의 생성자 매개변수를 생성되는 객체의 파생클래스에서 제공해야 하기 때문이다. »+ 즉, 파생클래스가 기본클래스의 생성자 매개변수의 리스트와 각 매개변수의 의미를 알고 직접 제공해야 한다! 귀찮..
- 배열을 생성할 때 : 일반적으로 배열의 요소 객체에 생성자 매개변수를 지정할 수 없다.
- 모든 클래스에 기본 생성자를 넣는다?
- 다른 멤버 함수가 복잡해진다. 객체의 멤버 데이터가 제대로 초기화 되었는지 보장할 수가 없다.
- 초기화하지 않은 멤버데이터에 임의의 값을 넣었을 때 -> 거의 모든 멤버 함수에 초기화 했는지(ex)ID번호) 체크해야 한다.
- 클래스 효율 저하 : 초기화 검사 시간만큼 실행속도 저하, 초기화 검사 코드만큼 실행파일 / 라이브러리 크기 증가, 초기화 검사 실패 처리 코드도 생각해야 한다.
- 즉 쓸데없는 상황에서는 기본 생성자를 피하자.