본문 바로가기
Programming/C++

C++ 언어 기초 (13) - 추상클래스, 다중상속

by 롱일스 2020. 9. 4.
반응형

▣ 추상 클래스

 ●  추상 클래스, 상세 클래스

  ▷ 추상 클래스, abstract class

  • 유사한 성격을 가지는 클래스들의 공통적인 요소를 뽑아 만든 클래스로, 일부 메소드가 구체적으로 구현되어 있지 않아 직접적인 사례가 존재하지 않는 클래스를 말한다.
  • 추상 클래스로 객체를 직접 정의할 수 없다.
  • 추상 클래스는 그 자체로 사용되지 않고 파생 클래스를 통해 구현되어 사용된다.
  • 사용 목적: 정 그룹에 속하는 클래스들(파생 클래스들)이 반드시 갖고 있어야하는 행위를 지정해서 필요한 행위를 잊지 않고 정의하도록 하기 위함

 

  상세 클래스, concrete class

  • 클래스의 모든 요소가 구체적으로 구현되어 직접적인 사례가 존재하는 클래스다.
  • 상세 클래스는 객체를 정의할 수 있다.

 

  ▷ 예시 - Shapes 클래스

  • Shapes에 속하는 클래스인 Triangle, Circle 클래스의 객체는 draw나 move 등의 메소드가 정의되어야 한다는 것을 추상클래스인 Shapes 클래스에서 지정한다.
  • Shapes 클래스 자체는 draw와 move 메소드를 정의할 수 없어서 객체를 정의할 수 없다.
  • 추상 클래스인 'Shapes'에서 구현되지 않은 상태로 상속받은 draw나 move를 상세 클래스에서 구체적으로 정의한다.
  • Circle과 Triangle 클래스의 객체를 정의할 수 있다.

 

  •    기초 클래스 Shapes (추상 클래스)
Shapes
- lineColor : int
+ setLineColor(c : int)
+ draw( )
+ move( x, y : double)
  •   파생 클래스 Circle (상세 클래스)
Circle
- cx, cy : double
- radius : double
+ Circle ( x, y, r : double )
+ draw( )
+ move ( dx, dy : double)
  •   파생 클래스 Triangle (상세 클래스)
Triangle
- x1, y1 : double
- x2, y2 : double
- x3, y3 : double
+ Triangle ( v[3][2] : double )
+ draw( )
+ move(dx, dy : double)

 

 ●  추상 클래스 선언

  ▷ 멤버함수 중에 순수가상함수가 포함된 클래스를 선언한다.

  • 순수 가상함수 - 구현 부분이 없는 가상함수
  • 순수 가상함수 선언 ( =0 에 주목하세요)
virtual RetType functionName(fParameterList) = 0;

  ▷ 예시

class AClass {			//추상 클래스
public:
    virtual void vf() = 0;     // 순수가상함수
    void f1() const
        { cout << "Abstract" << endl;}
};

 

 ●  상세 클래스 선언

  ▷ 순수가상함수를 포함하지 않는 클래스를 선언한다

  • 상속받은 순수 가상함수가 있다면 반드시 재정의해야 한다.
class AClass {			//추상 클래스
public:
    virtual void vf() = 0;     // 순수가상함수
    void f1() const
        { cout << "Abstract" << endl;}
};

class CClass : public AClass { // 상세 클래스
public:
    void vf() { cout << "순수가상함수" << endl;
    void f2() const
        { cout << "concrete class" << endl;}
};
  • AClass는 객체를 만들 수 없는 추상클래스고, CClass는 상세클래스로 객체를 만들 수 있다. 
  • 만약에 CClass에서 순수가상함수인 vf( )를 재정의 안했으면 CClass도 추상클래스가 되어서 객체를 만들 수 없게 된다.

 

 ●  예제 

  ▷ Shapes 클래스 명세

  • 2차원 도형에 해당하는 원을 나타내는 Circle 클래스와 삼각형을 나타내는 Triangle을 선언한다.
  • 이 클래스들은 모두 공통적으로 도형 Shapes이기 때문에 도형을 그리기 위한 선의 색과 도형 내부를 채워 칠하기 위한 색을 속성으로 가지고 있다.
  • 도형 객체를 (dx, dy)만큼 이동할 수 있고, 2차원 좌표상에서 원점을 기준으로 도형의 크기를 확대 및 축소할 수 있다.
  • ㅇㅇ이라는 객체를 만들어서 속성 객체로는 선의 색과 내부 영역을 색칠하기 위한 색을 표현하고, 그 값을 설정하고 읽을 수 있다.
  • 도형 객체를 만들면 현재 속성에 따라서 정의된다.
  • 도형 객체의 선 색과 채우기 색은 항상 변경할 수 있다.

 

  ▷ 그래픽 속성 클래스 - Graphic 클래스

메소드 설명
Graphic( ) 생성자
void setLineColor(const string&) 지정된 색으로 선 색 지정
void setFillColor(const string&) 지정된 색으로 내부 영역 색 지정
string getLineColor( ) 선 색 반환
string getFillColor( ) 내부 영역 색 반환
속성 설명
string lineColor 선 색 속성
string fillColor 내부 영역 색 속성
class Graphic {
    string lineColor;  //선 색 속성
    string fillColor;  //내부 영역 색 속성
public:
    //그래픽 속성 객체 생성자
    Graphic() : lineColor("black"), fillColor("white") {}
    Graphic(const string &lc, const string &fc) : lineColor(lc), fillColor(fc) {}
    
    //속성 지정 함수
    void setLineColor(const string &lc){
        lineColor = lc;
    }
    void setFillColor(const string &fc){
        fillColor = fc;
    }
    
    // 속성 값을 읽는 함수
    string getLineColor() const{
        return lineColor;
    }
    string getFillColor() const{
        return fillColor;
    }
};

extern Graphic curAttrib; //현재속성을 나타내는 전역 객체

 

  ▷ 도형 클래스 - Shape 클래스

메소드 설명
Shape( ) 생성자. 현재 속성에 따라 도형 생성
void setLineColor(const string&) 지정된 색으로 선 색 지정
void setFillColor(const string&) 지정된 색으로 내부 영역 색 지정
void move(double dx, double dy) (순수가상함수) 도형 중심 (dx, dy)만큼 이동
void scale(double s) (순수가상함수) 도형의 크기 원점 기준 s배
void draw( ) (순수가상함수)  도형 그리기

 

속성 설명
Graphic attrib 도형의 그래픽 속성
class Shape {
protected:
    Graphic attrib;   //그래픽 속성
public:
    // 현재 그래픽 속성에 따라 도형 객체 생성
    Shape() : attrib(curAttrib) {}
    
    // 선 색 속성 지정
    void setLineColor(const string &c) {
        attrib.setLineColor(c);
    }
    
    // 내부 영역 색 지정
    void setFillColor(const string &c) {
        attrib.setFillColor(c);
    }
    
    //순수가상함수
    virtual void move(double dx, double dy) = 0; //도형의 이동
    virtual void scale(double s) = 0;  //원점 기준 크기 조정
    virtual void draw() const = 0; //그리기 멤버함수
};
    

 

  ▷ 원 클래스 - Circle 클래스

메소드 설명
Circle(double x, double y, double r) 생성자
void move(double dx, double dy) 원을 (dx, dy)만큼 이동
void scale(double s) 원의 크기 원점 기준 s배
void draw( ) 원 그리기
속성 설명
double cx, cy 원의 중심 좌표
double radius 원의 반경
  • Circle.h
/* Circle.h */
...
class Circle : public Shape {
    double cx, cy;   //원의 중심 좌표
    double radius;   //원의 반지름
public:
    Circle(double x, double y, double r) : cx(x), cy(y), radius(r) {}
    //원 객체 생성
    
    void move(double dx, double dy);	//원 이동
    void scale(double s);		  //원 크기 조정
    void draw() const;			  // 원 그리기 
};

 

  • Circle.cpp
#include <iostream>
#include "Circle.h"
using namespace std;

// 원 중심 좌표 (dx, dy) 이동
void Circle::move(double dx, double dy) {
    cx += dx;
    cy += dy;
}

// 원 크기 좌표 원점 기준 s배 조정
void Circle::scale(double s){
    cx *= s;
    cy *= s;
    radius *= s;
}

// 원 그리기
void Circle::draw() const{
    cout << "원 그리기" << endl;
    cout << "(" << cx << ", " << cy << ")에서부터 ";
    cout << radius << "만큼 떨어진 모든 점을 ";
    cout << attrib.getLineColor() << "으로 그리고" << endl;
    cout << "안은 " << attrib.getFillColor();
    cout << " 색으로 채운다." << endl;
}

 

  ▷ 삼각형 클래스 - Triangle 클래스

메소드 설명
Triangle(double v[3][2]) 생성자
void move(double dx, double dy) 삼각형을 (dx, dy)만큼 이동
void scale(double s) 삼각형의 크기 원점 기준 s배
void draw( ) 삼각형 그리기
속성 설명
double x1, y1 삼각형 꼭지점 좌표
double x2, y2
double x3, y3
  • Triangle.h
...
class Triangle : public Figure {
    double x1, y1, x2, y2, x3, y3; //삼각형 세 꼭지점
    double radius;
public:
    Triangle(const double v[3][2]); 
    //그래픽 속성에 따라 삼각형 객체 생성
    // v: 3개의 꼭지점 좌표 배열
    
    void move(double dx, double dy);  //삼각형 이동
    void scale(double s);  //원점 기준 크기 조정
    void draw() const;  // 그리기 
};

 

  • Triangle.cpp
#include "Triangle.h"
...
//생성자 - 3 꼭지점 좌표 초기화
Triangle::Triangle(const double v[3][2]){
    x1 = v[0][0]; y1 = v[0][1];
    x2 = v[1][0]; y2 = v[1][1];
    x3 = v[2][0]; y3 = v[2][1];
}

// (dx, dy)만큼 3 꼭지점 이동
void Triangle::move(double dx, double dy) {
    x1 += dx; y1 += dy;
    x2 += dx; y2 += dy;
    x3 += dx; y3 += dy;
}

// 삼각형 좌표 s배 조정
void Triangle::scale(double s){
    x1 += s; y1 += s;
    x2 += s; y2 += s;
    x3 += s; y3 += s;
}

//삼각형 그리기
void Triangle::draw() const{
    cout << "삼각형 그리기" << endl;
    cout << "(" << x1 << ", " << y1 << "), ";
    cout << "(" << x2 << ", " << y2 << "), ";
    cout << "(" << x3 << ", " << y3 << ")의 좌표를 잇는 선분을 ";
    cout << attrib.getLineColor() << " 색으로 그리고" << endl;
    cout << "내부는 " << attrib.getFillColor();
    cout << " 색으로 채운다." << endl;
}

 

  ▷ 실행 - Main.cpp

#include <iostream>
#include <string>
#include "Graphic.h"
#include "Shape.h"
#include "Circle.h"
#include "Triangle.h"
using namespace std;

Graphic curAttrib("black", "white");  //현재 그래픽 속성

void drawFigs(const Shape * const figs[], int n)
{
    for (int i=0; i < n; i++){
        figs[i]->draw();
        cout << endl;
    }
}
    
int main()
{
    Shape *figs[2];
    figs[0] = new Circle(0, 20, 10);
    double v[3][2] = { {0,0}, {20,0}, {10,15} };
    curAttrib.setLineColor("red");
    curAttrib.setFillColor("yello");
    figs[1] = new Triangle(v);
    drawFigs(figs, 2); //모든 도형 그리기
    
    figs[0]->scale(2);  // 원 크기 조정
    figs[1]->move(5, 10);  // 삼각형 좌표 이동
    drawFigs(figs, 2);   // 모든 도형 그리기
    
    return 0;
}

 

  ▷ 실행 결과

 

 

 

▣ 다중상속

 ●  다중상속, multiple inheritance

  ▷ 2개 이상의 기초 클래스로부터 상속 받는 것을 말한다.

Student
- school : string
+ Student ( s : string)
+ printSchool( ) : void
Employee
- company : string
+ Employee ( c: string )
+ printCompany ( ) : void

둘다▼상속

Parttime
-
+Parttime( s : string, c : string )

 

  ▷ 다중상속 예제

  • Student.h
#ifndef STUDENT_H_INCLUDED
#define STUDENT_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

class Student {
    string school;
public:
    Student(const string& s) : school(s) {}
    void printSchool() const { cout << school << endl; }
};
#endif

 

  • Employee.h
#ifndef EMPLOYEE_H_INCLUDED
#define EMPLOYEE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

class Employee {
    string company;
public:
    Employee(const string& c) : company(c) {}
    void printCompany() const { cout << company << endl; }
};
#endif

 

  • Parttime.h

Student와 Employee 둘다한테 상속받는다.

#ifndef PARTTIME_H_INCLUDED
#define PARTTIME_H_INCLUDED
#include <string>
#include "Student.h"
#include "Employee.h"
using namespace std;

// Student와 Employee 둘다한테 상속받음
class Parttime : public Student, public Employee {
public:
    Parttime(const string& s, const string& c)
        : Student(s), Employee(c) {}
};
#endif

 

  • Main.cpp
#include "Parttime.h"

int main()
{
    Parttime jimin("Busan High School", "Bighit Corporation");
    jimin.printSchool();
    jimin.printCompany();
    
    return 0;
}

 

 ●  다중상속의 모호성 해결법

  ▷ 2개 이상의 기초 클래스로부터 같은 이름의 멤버 함수를 상속받은 경우

Student
- school : string
+ Student ( s : string)
+ print( ) : void
Employee
- company : string
+ Employee ( c: string )
+ print( ) : void

두 기초 클래스에서 print( ) 멤버함수의 이름이 동일하게 정의된 경우가 발생할 수 있다.

Parttime jimin("Busan High School", "Bighit Corporation");
jimin.print();  //오류! 모호성!

 

  ▷ 사용하고 싶은 함수의 클래스를 명확하게 명시해준다.

Parttime jimin("Busan High School", "Bighit Corporation");
jimin.Student::print();  //모호성 해결!

 

 ●  다중상속에서 공통 기초 클래스 중복 상속

  ▷ 1개의 기초 클래스가 여러번 상속된다.

  • 가장 기초 클래스인 Person에서 받은 name이 Student 클래스와 Employee 클래스를 통해서 Parttime 클래스로 넘어오기 때문에 name 데이터멤버가 2개가 존재하는 상태로 객체가 생성될 수 있다.
Person
- name : string
+ Person ( n : string)
+ print( ) : void

상▼속

Student Employee
- school : string - company : string
+ Student ( s : string)
+ print( ) : void
+ Employee ( c: string )
+print( ) : void

상▼속

Parttime
-
+Parttime( s : string, c : string )

 

  • 가상 기초 클래스로 상속하면 중복 상속을 방지할 수 있다.

 

  ▷ 예제

  • Person.h
#ifndef PERSON_H_INCLUDED
#define PERSON_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

class Person {
    string name;
public:
    Person(const string &n) : name(n) {}
    void print() const { cout << name; }
};
#endif

 

  • Student.h

 가상 기초 클래스를 virtual 로 지정해서 상속을 받는다.

#ifndef STUDENT_H_INCLUDED
#define STUDENT_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

//가상 기초 클래스로 Person을 상속받음
class Student : virtual public Person {
    string school;
public:
    Student(const string& n, const string& s) : Person(n), school(s) {}
    void print() const { 
        Person::printc();
        cout << " goes to " << school << endl; }
};
#endif

 

  • Employee.h

 가상 기초 클래스를 virtual 로 지정해서 상속을 받는다.

#ifndef EMPLOYEE_H_INCLUDED
#define EMPLOYEE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

class Employee : virtual public Person {
    string company;
public:
    Employee(const string& n, const string& c) : Person(n), company(c) {}
    void printCompany() const {
        Person::print();
        cout << " is employed by " << company << endl; }
};
#endif

 

  • Parttime.h

  가상기초클래스로부터 상속받을 때는 생성자에 가상기초클래스의 생성자를 명시적으로 꼭 호출해야 한다.

  멤버 함수가 중복되지 않는 클래스가 생성된다. 

#ifndef PARTTIME_H_INCLUDED
#define PARTTIME_H_INCLUDED
#include <string>
#include "Student.h"
#include "Employee.h"
using namespace std;

// Student와 Employee 둘다한테 상속받음
class Parttime : public Student, public Employee {
public:
    Parttime(const string& n, const string& s, const string& c)
        : Person(n), Student(n, s), Employee(n, c) {} //Person의 생성자를 명시적으로 꼭 호출해야 한다.
    void print() const {
      Student::print();
      Employee::print();
    }
};
#endif

 

728x90
반응형