일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- Visual Micro
- Array
- 수광 소자
- vector
- 컴퓨터 그래픽스
- 라인트레이서
- Arduino
- stl
- set
- 아두이노
- C언어
- 자료구조
- Deque
- WinAPI
- queue
- 시스템프로그래밍
- map
- list
- 아두이노 소스
- LineTracer
- 운영체제
- Algorithm
- Stack
- html
- priority_queue
- arduino compiler
- directx
- 아두이노 컴파일러
- 통계학
- c++
- Today
- Total
Kim's Programming
C++ - 클래스 상속(2/3) 본문
상속의 특성
C++상속의 특성
객체 지향이라는 똑같은 이론에 기반하더라도 각 언어별로 상속을 구현하는 방법과 수준에는 다소 차이가 있습니다. C++언어의 상속은 대체로 세 가지 정도로 특징을 요약할 수 있습니다.
- 하나의 기반 클래스로부터 여러 개의 클래스를 파생시킬 수 있습니다. 세포라는 기본적인 속 성과 호흡한다, 번식한다, 등의 동작을 가지는 생물로부터 동물을 파생시켜 움직인다는 동작을 추가할 수 있습니다. 동물은 생물의 모든 특성을 가지기 때문에 이런 파생이 가능합니다. 마찬가지로 생물로부터 식물이나 미생물도 파생 가능한데 둘 다 생물의 일종이기 떄문입니다.이렇게 되면 동물, 식물, 미생물은 공동의 조상인 생물로부터 물려받은 속성과 동작을 공유하게 됩니다. 물론 각 파생 클래스는 기반 클래스로부터 물려받은 속성 외에 각기 다른 속성들을 추가로 정의할 수 있습니다. 예를 들어 동물은 척추 유무, 심장의 구조, 이동한다, 먹는다가 추가될 것이고 식물은 떡잎의 개수, 광합성 등의 속성과 동작이 추가될 것입니다.
- 하나의 클래스로부터 파생될 수 있는 클래스의 개수에 제한이 없을 뿐만아니라 파생의 깊이에도 제한이 없을 뿐만 아니라 파생의 깊이에도 제한이 없습니다. 파생된 클래스로부터 새로운 클래스를 얼마든지 파생시킬 수 있습니다. 생물로부터 상속받은 동물은 포유류라는 새로운 클래스를 파생시킬 수 있으며 포유류는 또한 영장류의 부모가 될 수 있습니다. 각 파생 관계의 아래쪽으로 내려올수록 더 많은 속성과 동작이 정의될 것입니다. 파생관계의 위쪽에있는 클래스는 속성을 몇 개 가지지 않는 일반적인 사물을 표현하며 포괄하는 범위가 넓은 반면 아래쪽에 있는 클래스일수록 점점 더 특수하고 구체적인 사물을 표현합니다. 파생을 많이 할수록 더 많은 속성과 동작이 정의되므로 점점 특수해집니다.
기반, 파생 클래스 또는 부모, 자식이라는 용어는 상대적인 개념입니다. 한 클래스가 상속관계의 중간에 있다면 이 클래스는 부모에 대해서는 자식이지만 또한 자신의 자식에 대해서는 부모가 됩니다. 상속관계의 위쪽에 있는 클래스를 선조 또는 조상이라고 하며 아래쪽에 있는 클래스를 후손이라고 표현하기도 합니다. 또 최상위 클래스는 루트라고합니다. 부모 자식 클래스의 관계를 IS A관계하고 하는데 이는 자식 클래스가 일종의 부모 클래스란 뜻입니다. 예를 들어 동물은 생물의 일종이며 포유류는 또한 동물의 일종리라 할 수 있습니다. IS A라는 용어는 영문에서의 is a 를 뜻하는데 "동물은 일종의 생물이다"를 영어로 animal is a creature라고 표현하기 떄문입니다. 하지만 역관계는 성립하지 않는데 모든 생물을 동물이라고 할 수 없기 떄문입니다. - 비록 자주 사용되지는 않지만 C++은 두 개 이상의 클래스로부터 새로운 클래스를 파생시킬 수 있는데 이를 다중 상속이라고 합니다. 이때 파생되는 클래스는 기반 클래스들의 모든 속성을 물려받습니다. 사람이 부모의 속성을 동시에 물려받는 것과 마찬가지입니다. 다중 상속에 의해 여러 개의 클래스가 복잡한 상속 관계를 구성하는 크래프가 만들어지는데 이렇게 되면 상속 계층이 너무 복 잡해져서 잘 사용되지 않습니다. 이 외에도 상속에 대한 또 다른 제약으로 기본 타입으로부터 상속은 허가되지 않는다는 규칙이 있습니다. 즉, class MyInt : public int{ } 식으로 기본 타입인 int를 상속받아 새로운 클래스를 만들 수는 없다는 상식적인 이야기입니다. int같은 시스템 내장 타입은 클래스와 똑같이 취급되기는 하지만 실제로 클래스 선언문이 존재하는 것은 아니므로 기반 클래스로 사용할 수 없습니다. 다중 상속을 제외할 경우 클래스간의 계층 관계는 보통 트리형태로 그릴 수 있습니다.
이차 상속
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #define _USE_MATH_DEFINES #include<iostream> #include<math.h> #include<conio.h> #include<Windows.h> using namespace std; void Gotoxy(int x, int y) { COORD Position = { x, y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Position); } class Coord { protected: int x, y; public: Coord(int ax, int ay){ x = ax; y = ay; } void GetXY(int &rx, int &ry){ rx = x; ry = y; } void SetXY(int ax, int ay){ x = ax; y = ay; } }; class Point :public Coord { protected: char ch; public: Point(int ax, int ay, char ach) :Coord(ax, ay){ ch = ach; } void Show(){ Gotoxy(x, y); _putch(ch); } void Hide(){ Gotoxy(x, y); _putch(' '); } }; class Circle :public Point { protected: int Rad; public: Circle(int ax, int ay, char ach, int aRad) :Point(ax, ay, ach){ Rad = aRad; } void Show() { for (double a = 0; a < 360; a += 15) { Gotoxy(int(x + sin(a*M_PI / 180)*Rad), int(y - cos(a*M_PI / 180)*Rad)); _putch(ch); } } void Hide() { for (double a = 0; a < 360; a += 15) { Gotoxy(int(x + sin(a*M_PI / 180 * Rad)), int(y - cos(a*M_PI / 180)*Rad)); _putch(' '); } } }; void main() { Point P(10, 10, '@'); P.Show(); Circle C(40, 10, '*', 8); C.Show(); } | cs |
왼쪽 @문자가 점이고 오른쪽에는 원같지 않지만 원이 그려져 있습니다. 점,원은 그래픽 객체인데 콘솔창에서 억지로 표현을 하다보니 세로로 길쭉한 타원이 나왔습니다. Circle의 Show함수에는 원을 그리는 코드가 작성되어 있는데 원을 그리는 알고리즘은 그다지 복잡하지는 않지만 상속이야기 중이니 넘어가겠습니다.
객체의 생성 및 파괴
상속받은 멤버는 파생 클래스에서 직접 초 기화할 수 없으며 기반 클래스에게 초기화를 부탁해야합니다. 파생 클래스는 기반 클래스의 모든 멤버를 상속받기는 하지만 이 멤버를 어떻게 초기화해야하는지 정확하게 알지 못합니다. 또한 상 속받은 멤버 중 일부는 private 엑세스 속성을 가질 수도 있으므로 파생 클래스가 이 멤버를 초기화할 권한이 없습니다. 자식에게조차 공개하지 않겠다고 숨겨놓은 것이므로 파생 클래스는 부모의 private멤버에 대해서 관심을 가질 필요도 건들일 수도 없습니다. 대신 기반 클래스의 public생성자를 호출하여 상속받은 멤버를 초기화해야합니다. 생성자는 항상 public이므로 누구나 호출할 수 있습니다. 상속받은 멤버의 의미와 초기화 방법에 대해서 가장 정확하게 알고 있는 주체는 이 멤버를 정의한 클래스이므로 기반 클래스의 생성자를 이용하는 것이 합리적입니다. 파생 클래스가 기반 클래스의 생성자를 호출할때는 초기화 리스트를 사용해야합니다. 위의 소스에서 main 함수에 있는 Circle C(40,10,'*',8);선언문이 어떤 순서로 이 객체를 초기화하는지 순서대로 따라가보겠습니다.
- main에서 Circle 객체 C를 생성할 때 Circle의 생성자가 호출됩니다. Circle(40,10,'*',8)이 호출되며 생성자로 원 객체 생성에 필요한 인수들이 전달됩니다.
- 생성자의 본체가 실행되기 전에 초기화 리스트가 먼저 실행됩니다. 초기화 리스트에서 기반 클래스인 Point 생성자를 호출하며 이 생성자로 ax,ay,ach인수를 전달합니다.
- Point의 생성자는 다시 자신의 초기화 리 스트에 있는 Coord의 생성자를 호출하며 이 생성자로 ax,ay인수를 전달합니다. 이런 식으로 파생 클래스는 항상 기반 클래스의 생성자를 통해 상속받은 멤버를 초기화해야합니다.
- Coor의 생성자에서 x,y 멤버를 인수로 전달된 ax,ay로 초기화합니다. 이떄는 단순 타입이므로 초기화 리스트를 쓰지 않아도 상관없습니다. ax, ay는 40,10으로 전달되었으므로 x,y는 (40,10)좌표를 가리키도록 초기화될 것입니다.
- Coord 생성자가 리턴되면 Point의 생성자 본체에서 ch 멤버에 인수로 전달된 ach의 값 '*'을 대입합니다. Point는 자신의 고유 멤버를 초기화한 후 리턴합니다.
- Circle 생성자는 초기화 리 스트를 통해 상속받은 멤버의 초기화를 마치고 본체에서 자신의 고유 멤버인 Rad을 aRad 인수로 초기화합니다. 따라서 Rad는 8이 될 것입니다. Circle 생성자가 자신의 모든 멤버를 초기화하고 main으로 리턴하면 객체의 C의 초기화가 완료됩니다.
1 2 3 4 5 | Circle(int ax, int ay, char ach, int aRad) { Point(ax.ay.ach); Rad = aRad; } | cs |
1 2 3 4 5 | Circle(int ax, int ay, char ach, int aRad) { x=ax;y=ay;ch=ach; Rad=aRad; } | cs |
1 2 3 4 | Derive(인수들) : Base(상속받은 인수들) { 본체 - 여기서 자신의 고유 멤버 초기화 } | cs |
객체의 생성 및 파괴
- 생성자와 파괴자
- 대입 연산자
- 정적 멤버 변수와 정적 멤버 함수
- 프렌드 관계지정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include<iostream> class B { public: int m; B(int am) { m = am; } void f() { puts("Base Function"); } }; class D :public B { public: int m; D(int dm, int bm) :B(bm) { m = dm; } void f() { puts("Derived Function"); } }; void main() { D d(1, 2); printf("d.m = %d\n", d.m); d.f(); } | cs |
'Programming > Cplusplus' 카테고리의 다른 글
C++에서의 다양한 Casting 방법들 (0) | 2018.02.01 |
---|---|
네임스페이스(namespace)의 이용 (0) | 2016.03.07 |
C++ - 클래스 상속(1/3) (0) | 2015.09.22 |
C++ - 연산자 오버로딩(3/3) (0) | 2015.09.21 |
C++ - 연산자 오버로딩(2/3) (0) | 2015.09.20 |