출처 => C++ How to Program (Detitel / Prenticehall) / 현재 그리고 미래지향적인 C++ 프로그래밍(장석우, 임정목 / 비앤씨에듀케이션)
상속(Inheritance)이란 무엇인가?
▶ 기본 클래스의 속성과 기능을 파생 클래스에 물려주는 것
▶ 기본 클래스는 상속해주는 클래스, 부모클래스라고도 부른다. 파생클래스는 상속받는 클래스이며 자식클래스라고도 부른다.
▶ 기본클래스에서 파생 클래스로 갈수록 클래스의 개념이 구체화된다. (과일->사과, 자동차->BMW)
▶ 상속은 3가지의 장점이 있다.
1. 간결한 클래스 작성
2. 클래스 간의 계층적 분류 및 관리의 용이함
3. 클래스의 재사용과 확장을 통한 소프트웨어 생산성 향상
상속 관계의 클래스 -Point클래스를 상속받는 ColorPoint클래스 예제
#include<iostream>
#include<string>
using namespace std;
class Point {
int x, y;
public:
void set(int x, int y) {
this->x = x;
this->y = y;
}
void showPoint() {
cout << "(" << x << "," << y << ")" << endl;
}
};
class ColorPoint :public Point {
string color;
public:
void setColor(string color) {
this->color = color;
}
void showColorPoint();
};
void ColorPoint::showColorPoint() {
cout << color << ".";
showPoint();
}
void main() {
Point p;
ColorPoint cp;
cp.set(3, 4);
cp.setColor("Red");
cp.showColorPoint();
}
소스 코드를 분석해보자.
1. Point 클래스
▶ Point 클래스에서의 멤버 변수는 int 형의 x와 y이다.
▶ set(int x, int y)메소드는 set 메소드 안에서 선언된 x와 y를 Point클래스의 x와 y에 대입을 해주는 역할을 한다.
▶ showPoint( )메소드는 set함수에서 선언한 x와 y를 출력해주는 메소드이다. 클래스 안에서 선언을 하게 되면 엑세스 할 수 없습니다는 오류가 발생하기 때문에 선언만 클래스 안에서 해주고 세부 내용은 밖에서 정의해야 한다.
2. ColorPoint 클래스
▶ ColorPoint 클래스는 public의 형태로 Point 클래스를 상속받는다.
▶ setColor(string color) 메소드는 setColor(string color)메소드에서 선언된 color을 ColorPoint클래스의 color에 대입해준다.
▶ showColorPoint( )메소드는 setColor메소드에서 대입한 color의 값을 출력해주는 역할을 한다.
▶ showPoint( )는 Point 클래스의 메소드. 상속을 받았기 때문에 사용이 가능하다. (단, ColorPoint의 메소드는 Point클래스에서는 사용이 불가하다!!!)
상속과 객체 포인터
1. 업 캐스팅(up-casting)
▶ 파생 클래스의 객체를 기본 클래스의 포인터로 가리키는 것을 말한다.
▶ 파생클래스의 객체를 기본 클래스의 객체처럼 다룰 수 있게 한다.
#include<iostream>
#include<string>
using namespace std;
class Circle {
public:
int radius;
public:
void setRadius(int r) {
radius = r;
}
void showRadius() {
cout << "반지름" << radius << endl;
}
};
class Pizza :public Circle {
public:
double getArea();
};
double Pizza::getArea() {
return radius * radius * 3.14;
}
void main() {
Circle c;
c.setRadius(5);
c.showRadius();
//반지름 알려주는 과정
Pizza hp;
Pizza* cDer = &hp;
Circle* cBase = cDer;//업캐스팅
cBase->setRadius(100);
cout < cDer->getArea() << endl;
cBase->getArea();
}
▶ 이는 오류가 발생한다.!!!
Pizza hp;
Pizza* cDer = &hp;
Circle* cBase = cDer;//업캐스팅
▶ cBase는 Circle 클래스의 포인터이므로 cBase 포인터로는 Circle 클래스 멤버만 접근할 수 있다.
▶ 그러므로 getArea()함수는 Circle 클래스의 멤버가 아니라 Pizza 클래스에서 선언된 멤버이므로 오류가 발생한다.
2. 다운 캐스팅(Down Casting)
▶ 기본 클래스 포인터가 가리키는 객체를 파생 클래스의 포인터로 가리키는 것을 다운 캐스팅이라고 한다.
▶ 자식 형식에서 부모 형식을 사용하는 것이며 업 캐스팅에서 다시 원래의 형으로 되돌려주는 작업이다.
#include<iostream>
#include<string>
using namespace std;
class Circle {
public:
int radius;
public:
void setRadius(int r) {
radius = r;
}
void showRadius() {
cout << "반지름" << radius << endl;
}
};
class Pizza :public Circle {
public:
double getArea();
};
double Pizza:: getArea() {
return radius * radius * 3.14;
}
void main() {
Circle c;
c.setRadius(5);//반지름 입력
c.showRadius();//반지름 출력
Pizza hp;
hp.setRadius(10);
cout << "햄 피자 면적:" << hp.getArea() << endl;
Pizza cheesepizza;
Pizza* cDer = &cheesepizza; //치즈피자의 주소값을 cDer에 입력
Circle* cBase = cDer;//업 캐스팅
cBase->setRadius(100);
cBase->showRadius();
cDer = (Pizza*)cBase;
cDer->setRadius(1);
cDer->showRadius();
cout << "치즈 피자면적:" << cDer->getArea() << endl;
}
상속과 생성자
▶ 파생 클래스의 객체는 기본 클래스의 생성자를 무조건적으로 호출한다. (소멸자도 역시 호출)
▶ main 메소드에서는 B클래스의 객체인 b(5)만 선언했을 뿐인데 생성자 A( )가 호출되었음을 알 수 있다.
▶ 파생 클래스의 생성자가 기본 클래스의 기본 생성자를 선택하는 방법은 크게 두 가지가 있다.
1. 묵시적으로 기본 클래스의 기본 생성자를 선택하는 경우
#include<iostream>
using namespace std;
class A {
public:
A() {
cout << "생성자 A" << endl;
}
A(int x) {
cout << "매개변수생성자 A" << endl;
}
};
class B :public A {
public:
B() {
cout << "생성자 B" << endl;
}
B(int x) {
cout << "매개변수생성자 B" << endl;
}
};
void main() {
B b(5);
}
2. 기본 클래스의 생성자를 명시적으로 선택하는 경우
#include<iostream>
using namespace std;
class A {
public:
A() {
cout << "생성자 A" << endl;
}
A(int x) {
cout << "매개변수생성자 A" << endl;
}
};
class B : public A {
public:
B() {
cout << "생성자 B" << endl;
}
B(int x) :A(x+3){
cout << "매개변수생성자 B" << endl;
}
};
void main() {
B b(5);
}
상속 지정 (public, private, protected)
▶ 상속을 선언할 때는 public, private, protected 이 세 가지 방법으로 지정할 수 있다. 지금까지 위에 있는 예시들은 모두 public형태로 상속을 한 것들이다.
▶ 어떤 식으로 상속하느냐에 따라 멤버들의 범위도 바뀐다.
▶ public -> 기본 클래스의 public, private, protected 멤버 속성을 그대로 계승한다.
▶ protected -> 기본 클래스의 protected, public 멤버를 protected로 계승한다.
▶ private -> 기본 클래스의 protected, public 멤버를 private으로 계승한다.
1. public으로 지정하여 상속하는 예제
#include<iostream>
using namespace std;
class Base {
public:
int a = 3;
void publicOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
protected:
int b = 4;
void protectedOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
private:
int c = 5;
void priavateOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
};
class Derived :public Base {
public:
int d = 13;
void publicOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
protected:
int e = 14;
void protectedOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
private:
int f = 15;
void priavateOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
};
void main() {
Derived d;
cout << "Base의 public 변수는 " << d.a << endl;
cout << "Base의 protected 변수는" << d.b << endl;//오류 발생
cout << "Base의 private 변수는 " << d.c << endl;//오류 발생
cout << "Derived의 public 변수는 " << d.d << endl;
cout << "Derived의 protected 변수는" << d.e << endl;//오류 발생
cout << "Derived의 private 변수는 " << d.f << endl;//오류 발생
d.publicOfBase();
d.protectedOfBase();//오류 발생
d.privateOfBase();//오류 발생
d.publicOfDerived();
d.protectedOfDerived();//오류 발생
d.privateOfDerived();//오류 발생
}
<설명>
▶ 위의 소스코드는 public 형태로 기본 클래스를 상속받은 경우이다.
▶ 먼저 main() 함수를 보자
void main() {
Derived d;
cout << "Base의 public 변수는 " << d.a << endl;
cout << "Base의 protected 변수는" << d.b << endl;//오류 발생
cout << "Base의 private 변수는 " << d.c << endl;//오류 발생
cout << "Derived의 public 변수는 " << d.d << endl;
cout << "Derived의 protected 변수는" << d.e << endl;//오류 발생
cout << "Derived의 private 변수는 " << d.f << endl;//오류 발생
d.publicOfBase();
d.protectedOfBase();//오류 발생
d.privateOfBase();//오류 발생
d.publicOfDerived();
d.protectedOfDerived();//오류 발생
d.privateOfDerived();//오류 발생
}
▶ 주석처리된 부분은 오류가 난 부분이다. protected 형태는 지정된 클래스와 상속받은 클래스에서만 사용가능하고, private 형태는 지정된 클래스에서만 사용가능하다. 따라서 main( ) 메소드에서는 외부함수이므로 사용이 불가능하여 public 형태만 출력이 가능한 것이다.
2. protected으로 지정하여 상속받는 예제
#include<iostream>
using namespace std;
class Base {
public:
int a = 3;
void publicOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
protected:
int b = 4;
void protectedOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
private:
int c = 5;
void priavateOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
};
class Derived :protected Base {
public:
int d = 13;
void publicOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
protected:
int e = 14;
void protectedOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
private:
int f = 15;
void priavateOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
};
void main() {
Derived d;
cout << "Base의 public 변수는 " << d.a << endl;//오류 발생
cout << "Base의 protected 변수는" << d.b << endl;//오류 발생
cout << "Base의 private 변수는 " << d.c << endl;//오류 발생
cout << "Derived의 public 변수는 " << d.d << endl;
cout << "Derived의 protected 변수는" << d.e << endl;//오류 발생
cout << "Derived의 private 변수는 " << d.f << endl;//오류 발생
d.publicOfBase();//오류 발생
d.protectedOfBase();//오류 발생
d.privateOfBase();//오류 발생
d.publicOfDerived();
d.protectedOfDerived();//오류 발생
d.privateOfDerived();//오류 발생
}
▶ main 함수를 보자. 변화가 생긴듯 하다.
void main() {
Derived d;
cout << "Base의 public 변수는 " << d.a << endl;//오류 발생
cout << "Base의 protected 변수는" << d.b << endl;//오류 발생
cout << "Base의 private 변수는 " << d.c << endl;//오류 발생
cout << "Derived의 public 변수는 " << d.d << endl;
cout << "Derived의 protected 변수는" << d.e << endl;//오류 발생
cout << "Derived의 private 변수는 " << d.f << endl;//오류 발생
d.publicOfBase();//오류 발생
d.protectedOfBase();//오류 발생
d.privateOfBase();//오류 발생
d.publicOfDerived();
d.protectedOfDerived();//오류 발생
d.privateOfDerived();//오류 발생
}
▶ d.publicOfBase()메소드와 d.a에 오류가 발생하였다.
▶ 지금 위의 예제는 Base클래스를 protected의 형태로 상속받는 상황이다. protected는 지정된 클래스와 상속받은 클래스에서만 사용이 가능하다. 그러기에 main()함수는 외부함수이므로 Base 클래스에 접근을 할 수가 없다. 따라서 Base의 클래스 안에 있는 변수 a와 publicOfBase는 출력될 수 없다.
3. private로 지정하여 상속받는 예제
#include<iostream>
using namespace std;
class Base {
public:
int a = 3;
void publicOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
protected:
int b = 4;
void protectedOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
private:
int c = 5;
void priavateOfBase() {
cout << "Base의 public 메소드입니다" << endl;
}
};
class Derived :private Base {
public:
int d = 13;
void publicOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
protected:
int e = 14;
void protectedOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
private:
int f = 15;
void priavateOfDerived() {
cout << "Derived의 public 메소드입니다" << endl;
}
};
void main() {
Derived d;
cout << "Base의 public 변수는 " << d.a << endl;//오류 발생
cout << "Base의 protected 변수는" << d.b << endl;//오류 발생
cout << "Base의 private 변수는 " << d.c << endl;//오류 발생
cout << "Derived의 public 변수는 " << d.d << endl;
cout << "Derived의 protected 변수는" << d.e << endl;//오류 발생
cout << "Derived의 private 변수는 " << d.f << endl;//오류 발생
d.publicOfBase();//오류 발생
d.protectedOfBase();//오류 발생
d.privateOfBase();//오류 발생
d.publicOfDerived();
d.protectedOfDerived();//오류 발생
d.privateOfDerived();//오류 발생
}
'💻 개인공부 💻 > C | C++' 카테고리의 다른 글
[C++] 제네릭 함수 (feat. template) (0) | 2020.06.21 |
---|---|
[C++] 상속(Inheritance) - 2 (feat. 가상 함수와 추상클래스) (0) | 2020.06.20 |
[C++] 연산자 중복 (feat. 프랜드 함수) (0) | 2020.06.19 |
[C++] 프랜드 함수 (0) | 2020.06.19 |
[C++] 생성자와 소멸자 (0) | 2020.06.18 |