상속 실무 예제

구대호·2021년 11월 26일
0

<가상함수(virtual function)의 필요성>

  • 클래스를 상속 받아서 쓰는데, 상속 받은 클래스의 함수 중 고치고 싶은 함수가 있을 때
  • 기존 클래스의 모든 내용을 모두 현재 프로그램에 사용할 수 있는데, 특정 함수가 마음에 들지 않음
  • 기존의 클래스를 고치느니 상속을 받고, 가상함수를 이용하여 기존의 함수를 고쳐서 사용
  • 상속 받은 함수(부모)를 실행하지 않고 자신(자식)의 함수를 실행
  • 가상함수의 구현을 중복(overriding)이라고 함

<오버라이딩: 가상함수 구현>

  • 가상함수는 기본(부모) 클래스 내에서 정의된 멤버함수를 파생(자식) 클래스에서 재정의하고자 할 때 사용한다.
  • 기본 클래스의 멤버함수와 같은 이름의 함수를 파생 클래스에서 재정의하여 사용한다.
  • 파생 클래스 내에서 가상함수의 재정의는 함수 중첩 기능과 비슷하지만 파생 클래스에서 재정의되는 가상함수는 기본 클래스와 함수의 리턴값, 매개변수
    개수, 형이 완전히 같아야 한다.
  • 가상함수의 구현을 중복(overriding)이라 한다.

overloading vs. overriding

//overloading, 함수중첩
class A {
private:
	int age;
public:
	A() { age = 1; }
	A(int a) { age = a; }
};
//overriding, 가상함수
class A
{
public:
	virtual int SS(int i) { return(i * 2); }
};
class B : public A
{
public:
	int SS(int i) { return(i * 3); }
};

<바인딩(binding)>

  • 변수와 함수에 대한 메모리 할당이나 함수 호출이 발생했을 때 실제로 처리해야 하는 명령어들이 결정되는 시점
  • 정적(static) 바인딩, early
컴파일시 변수의 위치와 함수가 실행할 명령이 결정되는 경우
static 변수, overloading
  • 동적(dynamic) 바인딩, late
실제 실행할 때(run time) 결정되는 경우
지역 변수, overriding

<동적바인딩(지역 변수)과 정적바인딩(static변수)>

#include <iostream>
using std::cout;
void sub();
int main()
{
	cout << "start\n";
	sub();
	sub();
	sub();
	return 0;
}
void sub()
{
	int x = 10;
	//동적 바인딩, run-time시
	static int y = 10;
	//정적 바인딩, y의 초기값은 컴파일시 10으로
	//정해지며 실행시에 이 선언문은 실행하지 않음
	cout << x << y << '\n';
	x++;
	y++;
}
//start
//1010
//1011
//1012

<정적(static) 멤버변수>

class Point {
	int x;
	int y;
	static int count;
public:
	Point(int i, int j) { x = i; y = j; }
	int getX() { return x; }
	int getY() { return y; }
};
  • 클래스의 멤버 변수 선언문에 static이라는 키워드 붙임
  • C언어의 static변수와 의미가 다름
  • 객체들이 정보를 공유할 목적으로 사용하는 변수

<정적 멤버변수의 특징>

  • 모든 객체가 공유하는 멤버변수
  • 한 클래스로부터 객체가 여러 개 만들어지더라도 이 멤버변수는 하나만 생성됨
  • 여러 객체들에서 공유해야 하는 정보는 정적 멤버변수로 선언
class Dog {
private:
	static int age;
public:
	int getAge() { return age; }
	void setAge(int a) { age = a; }
};
int main()
{
	Dog merry, happy;
	:
}

<정적(static) 멤버변수>

#include <iostream>
using std::cout;
class Point {
	int x;
	int y;
	static int count; //선언
public:
	Point() { cout << ++count; }
	~Point() { cout << --count; }
};
int Point::count = 0; //정의
int main()
{
	Point p1, p2, p3;
	return 0;
}
//123210

<가상함수(virtual function)>

  • 오버라이딩을 구현하는 방법
  • 동적(실행시) 바인딩, late binding
  • 가상함수를 정의하기 위해서는 기본 클래스의 멤버함수 앞에 'virtual'이라는 키워드를 씀
  • 가상함수는 하나의 이름(인터페이스)으로 여러 개의 수단과 방법을 제공하는 객체지향 프로그래밍의 다형성을 구현하는 한 방법
  • 기본 클래스에서 상속 받았는데 파생 클래스에서 멤버함수를 재정의하는 이유는 어떠한 경우라도 기본클래스의 멤버함수를 버리고 파생 클래스의 멤버함수를 사용하려는 것
  • 기존 클래스의 모든 내용을 모두 현재 프로그램에 사용할 수 있는데 특정 함수가 걸맞지 않을 경우, 기존의 클래스를 고치느니 상속을 받고 가상함수를 이용하여 마음에 들지 않는 함수만 고쳐서 사용함

<가상함수 형식>

class 클래스명 {
	virtual 리턴형 함수명(매개변수) { }
	:
};
class A
{
public:
	virtual int SS(int i) { return(i * 2); }
	//자식아! 이 함수가 마음에 들지 않으면 재정의해라. 그럼 이 함수는 무시될 거야.
	//그런데 함수명, 리턴형, 매개변수의 개수와 자료형은 같아야 하고 기능만 다르게 만들어야 한다.
};
class B : public A
{
public:
	int SS(int i) { return(i * 3); }
	//부모클래스 A의 SS함수가 마음에 들지 않아 자식클래스에서 재정의
};

static binding(overloading)

#include <iostream>
using std::cout;
using std::endl;
class A
{
public:
	int ss(int i) { return(i * 2); }
	int dd(int i) { return(ss(i) * 2); }
};
class B : public A
{
public: //부모클래스 A의 ss함수가 마음에 들지 않아 자식클래스에서 재정의
	int ss(int i) { return(i * 3); } // 전혀 사용되지 않음
};
int main()
{
	B bb;
	A* pA = new A;
	A* pB = new B; //부모 클래스의 포인터는 자식 클래스를 가리킬 수 있음
	cout << bb.dd(2) << endl;
	cout << pA->dd(2) << endl;
	cout << pB->dd(2) << endl;
	delete pA; delete pB;
	return 0;
}
//8
//8
//8

dynamic binding(overriding)

#include <iostream>
using std::cout;
using std::endl;
class A
{
public:
	virtual int ss(int i) { return(i * 2); }
	//클래스 A의 멤버함수 ss는 자식 클래스에서 재정의 할 경우 무시하세요.
	int dd(int i) { return(ss(i) * 2); }
};
class B : public A
{
public: //부모클래스 A의 ss함수가 마음에 들지 않아 자식클래스에서 재정의
	int ss(int i) { return(i * 3); }
};
int main()
{
	B bb;
	A* pA = new A;
	A* pB = new B; //부모 클래스의 포인터는 자식 클래스를 가리킬 수 있음
	cout << bb.dd(2) << endl;
	cout << pA->dd(2) << endl;
	cout << pB->dd(2) << endl;
	delete pA; delete pB;
	return 0;
}
//12
//8
//12

<순수가상함수(pure virtual function)>

#include <iostream>
using std::cout;
using std::endl;
class Shape {//추상클래스(abstract class)
protected:
	double x, y;
public:
	virtual double area() { return 0; } //주석처리
	// 일반 가상 함수
	// virtual double area()=0; //주석 해제
}; // 순수 가상 함수
class Rectangle : public Shape {
private:
	double height, width;
public:
	Rectangle(double h, double w)
	{
		height = h; width = w;
	}
	double area() { return(width * height); }
};
class Triangle : public Shape {
private:
	double height, width;
public:
	Triangle(double h, double w)
	{
		height = h; width = w;
	}
	double area() { return(width * height / 2.0); }
};
int main()
{
	Shape ss; // virtual double area()=0;
	// 윗줄 주석과 같이 순수 가상함수가 있으면 추상클래스이고
	// 추상클래스는 객체(인스턴스)를 만들 수 없음
	Shape* p; //포인터 객체는 가능
	Rectangle nemo(10.0, 20.0);
	Triangle semo(10.0, 20.0);
	p = &nemo; //자식의 주소를 부모 포인터에 대입
	cout << "네모면적:" << p->area() << endl; //Rectangle::area()
	p = &semo;
	cout << "세모면적:" << p->area() << endl; //Triangle::area()
	return 0;
}

<virtual 있을 때와 없을 때의 차이>

#include <iostream>
using std::cout;
class Dot {
public:
	virtual void draw() {cout << "Dot::draw()\n";}
	void print() {
		cout << "Dot 클래스\n";
		draw();
		}
	};
	class Line :public Dot {
public:
	void draw() {cout << "Line::draw()\n";}
	};
int main() {
	Line line;
	line.print();
	return 0;
}
//Dot 클래스
//Line::draw()
#include <iostream>
using std::cout;
class Dot {
public:
	void draw() {cout << "Dot::draw()\n";}
	void print() {
		cout << "Dot 클래스\n";
		draw();
		}
	};
	class Line :public Dot {
public:
	void draw() {cout << "Line::draw()\n";}
	};
int main() {
	Line line;
	line.print();
	return 0;
}
//Dot 클래스
//Dot::draw()
profile
개발 초보 대학생

0개의 댓글