💻 개인공부 💻/C | C++

[C++] 상속(Inheritance) - 1 (feat. 개념, 상속 접근 지정자)

공대생 배기웅 2020. 6. 19. 21:46
반응형

출처 => C++ How to Program (Detitel / Prenticehall) / 현재 그리고 미래지향적인 C++ 프로그래밍(장석우, 임정목 / 비앤씨에듀케이션)

https://lesslate.github.io/cpp/%EC%97%85-%EC%BA%90%EC%8A%A4%ED%8C%85-%EB%8B%A4%EC%9A%B4-%EC%BA%90%EC%8A%A4%ED%8C%85/

 

상속(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();//오류 발생
}
728x90
반응형