* c++ 스타일 초기화
자료형 변수명(값);
ex) int num(20);
대입 연산과 똑같이 일어난다
* 클래스명 객체명1(객체명2)
ex) SoSimple sim2(sim1);
1. SoSimple형 객체를 생성
2. 객체의 이름은 sim2
3. sim1을 인자로 받을 수 있는 생성자의 호출을 통해서 객체 생성
* 디폴트 복사 생성자
복사 생성자를 정의하지 않으면
멤버 대 멤버의 복스를 진행하는
디폴트 복사 생성자를 삽입한다
참고로 복사 생성자는
클래스명(const 클래스명 &객체명) : 변수명(객체명.변수명) {}
이렇게 진행한다
ex) SoSimple(const SoSimple ©) : num1(copy.num1), num2(copy.num2) {}
ex)
Person Lee = Jun 코드는
Person Lee(Jun)으로 자동 변환된다 (생성자가 자동 호출된다고 생각)
but 얕은 복사라서
객체 소멸 시 문제가 된다
* 얕은 복사와 깊은 복사
얕은 복사
의존적인 복사
복사할 때 개체가 참조 타입의 멤버만 가지고 있는 경우
동적 할당을 받은 변수의 주소값을 공유한다
ex) Car(const Car & copy) {}
깊은 복사
전체 복사라 생각
객체가 가진 모든 멤버를 복사하는 것
참조값의 복사가 아니라 참조된 객체 자체가 복사됨
새로 동적 할당을 받고 원본의 데이터를 복사한다
ex)
Car(const Car & copy) {
this->size = copy.size;
this->data = copy.data;
}
* 복사 생성자가 호출되는 시점
1. 기존에 생성된 객체를 이용해서 새로운 객체를 초기화 하는 경우
2. call by value 방식의 함수 호출 과정에서 객체를 인자로 전달하는 경우
3. 객체를 반환하되, 참조형으로 반환하지 않는 경우
즉, 메모리 공간의 할당과 초기화가
동시에 일어나는 상황
* 상속
요구 사항의 변경 및 기능의 추가에 따른
변경을 최소화하기 위함
용어는
1. 상위 클래스 // 하위 클래스
2. 기초 클래스 // 유도 클래스
3. 슈퍼 클래스 // 서브 클래스
4. 부모 크래스 // 자식 클래스
상속 방법은
class 자식클래스명 : 상속접근제어자 부모클래스명 {}
ex) class Student : public Person {}
ㄴ Person 클래스를 상속하는 Student 클래스
* 자식 클래스의 생성자 정의
자식 클래스는 이니셜라이저를 통해서
부모 클래스의 생성자를 명시적으로 호출해야 한다
명시적으로 하는 것이 핵심
ex)
Student(char * myname, int myage, char * mymajor) : Person(myage, myname) {}
* 상속에서의 생성자, 소멸자 호출 순서
1. 부모 클래스 생성자
2. 자식 클래스 생성자
3. 자식 클래스 소멸자
4. 부모 클래스 소멸자
즉 생성자는
부모 클래스 먼저
소멸자는
자식 클래스 먼저
* 상속 형태
1. public 상속
2. protected 상속
3. private 상속
public 상속
접근 제어 권한을 그대로 상속한다
단, 부모의 private은 접근 불가로 상속한다
protected 상속
protected보다 접근 범위가 넓은 범위는 protected로 상속한다
즉 public을 protected로 상속한다
단, 부모의 private은 접근 불가로 상속한다
private 상속
private보다 접근 범위가 넓은 범위는 private으로 상속한다
즉 모든 형태를 private으로 상속한다
단, 부모의 private은 접근 불가로 상속한다
* IS-A 관계
상속의 기본 조건
스마트폰 is a 전화기
스마트폰이 자식 클래스
전화기가 부모 클래스
전화기에 기능을 추가한 것이 스마트폰이다
노트북 is a 컴퓨터
노트북이 자식 클래스
컴퓨터가 부모 클래스
컴퓨터에 기능을 추가한 것이 노트북이다
* HAS-A 관계
포함의 기본 조건
상속 관계가 x
경찰 has a 총
경찰 클래스가 총 크래스를 갖는다
* 객체 포인터 변수
부모 클래스형 포인터 변수는
부모 객체와 자식 객체를 모두 가리킬 수 있다
반대는 불가능
즉 아래로 가리키는 것은 가능하나
위로 가리키는 것은 불가능하다
컴파일러 입장에서
자식 클래스 객체들을 모두
부모 클래스 객체로 간주해도 무방하다는 것
c++ 컴파일러는 포인터 연산에서
포인터의 자료형을 기준으로 연산 가능성 여부를 판단하지
포인터가 실제 가리키는 객체의 자료형을 기준으로 판단하지 않는다
즉 부모는 자식을 가리킬 수 있으나
자식은 부모를 가리킬 수 없다
* 함수의 오버라이딩과 포인터 형
함수를 호출할 때 사용된 포인터 형에 따라서
호출되는 함수가 결정된다
포인터의 형에 정의된 함수가 호출된다
* 가상 함수 virtual function
부모를 상속받은 자식 클래스에서
재정의할 것을 기대하고 정해놓은 함수
오버라이딩 된 함수가 가상 함수이면
오버라이딩 한 함수도 자동으로 가상 함수이다
키워드 virtual 사용
virtual 자료형 함수명() {}
ex) virtual int func() {}
순수 가상 함수
몸체가 정의되지 않은 함수
추상 클래스
하나 이상의 순수 가상 함수를 멤버로 두어서
객체 생성이 불가능한 클래스
* 가상 소멸자
소멸자를 가상으로 선언함으로써
각각의 생성자 내에서 할당된 메모리 공간을
효율적으로 해결할 수 있다
* 참조 가능성
부모 클래스형 참조자는
자신의 객체 or 자식 클래스형 객체를 참조할 수 있다
즉, 부모는 자식을 참조할 수 있다
반대는 불가능
* 디폴트 대입 연산자
디폴트 대입 연산자는 부모 클래스의 대입연산자를 호출한다
명시적으로 대입 연산자를 정의하면
부모 클래스의 대입 연산자 호출도 별도로 명시해야 한다
* 템플릿
형이 하나인 경우
template <typename T>
T func(T num1, T num2) {}
형이 2개 이상인 경우
template <typename T1, typename T2>
T func(T num1, T2 num2) {}
typename 대신 class 키워드를 쓸 수 있다
호출은
함수명<자료형> ();
ex) func<int>(10);
ex) func<int, char>(10);
* 함수 템플릿의 특수화
특수한 상황에서의 함수
ex) 대소 비교 템플릿 함수인데 문자열이 입력된 경우
-> 이 때는 문자열 길이 비교용 특수 템플릿 사용
함수의 오버로딩을 생각
ex)
template <typename T>
T max(T a, T b) {}
template <> // 이 부분
char * max(char * a, char * b) {}
* 클래스 템플릿
템플릿을 먼저 선언하고
클래스 정의 안에 템플릿 자료형을 사용한다
다만 클래스 템플릿의 객체 생성시
자료형의 정보는 생략이 불가능하다
명시해줘야 한다
ex) Car<int> myCar();
* 클래스 템플릿의 선언과 정의의 분리
클래스 템플릿 안에 함수 선언만 하고
클래스 템플릿 외부에 함수를 정의한다
ex)
template <typename T>
class Car {}
template <typename T> // 이 부분
T Car::speedUp() {}
* 문자열
문자열 형식은
char name[10]
매개변수로 문자열을 받을 때는
getName(char * myname)
이런 식으로
하지만 종종 char *이 아니라
const char *으로 받아야 할 때가 있다
그래야 문자열 상수로 인식하기 때문이다
ex) getName(const char * name)
* ->에 대하여
객체의 멤버 함수를 사용하는 방법은
1. 객체명.함수명()
2. 객체포인터주소명->함수명()
ex) ljj.getName();
ex) ptr_ljj->getName();