차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판 이전 판
다음 판
이전 판
activity:public:2021:cpp:210803 [2021/08/03 21:41:27] – [예제] yhy01625activity:public:2021:cpp:210803 [2021/08/10 18:47:39] (현재) – [C++ 스터디 #12: 클래스(2)] bkparks12
줄 1: 줄 1:
 +======C++ 스터디 #12: 클래스(2)======
  
 +| 시간 | 2021년 8월 2일 화요일 20:00 ~ 22:00 |
 +| 장소 | ZOOM |
 +| 참가자 | - |
 +{{youtube>ldmFImj3h5g?medium}}
 +=====1. 소멸자=====
 +생성된 객체가 범위를 벗어날때 소멸자를 호출한다. 소멸자는 ''~'' + ''클래스 이름'' 으로 만든다. \\
 +소멸자는 파일을 닫거나 메모리를 반환하는 작업과 같이 프로그램을 종료하기 전 자원을 반납하는데 사용된다. \\
 +
 +<sxh cpp>
 +
 +
 +class MyString
 +{
 +private: 
 +    char* s;
 +    int size;
 +
 +public:
 +    MyString(char* c)
 +    {
 +        size = strlen(c) + 1;
 +        s = new char[size];
 +        strcpy(s, c);
 +    }
 +    ~MyString()
 +    {
 +        delte[]s;
 +    }
 +};
 +</sxh>
 +여기서 ~MyString이 소멸자이다. 
 +
 +=====2. 객체와 함수=====
 +====2.1. 객체를 함수로 전달하기====
 +
 +<sxh cpp>
 +#include<iostream>
 +using namespace std;
 +
 +class circle
 +{
 +public:
 +    circle(int s) : size(s) {}
 +    int size;
 +};
 +
 +void makedouble(circle c)
 +{
 +    c.size *= 2;
 +}
 +
 +int main()
 +{
 +    circle circle1(10);
 +    makedouble(circle1);
 +    cout << circle1.size << endl;
 +
 +    return 0;
 +}
 +
 +</sxh>
 +결과 : 10\\
 +
 +이 결과를 보면 ''c.size *= 2;'' 를 했음에도 size는 변경되지 않았다. 위의 예에서 main()의 객체인 circle1은 makedouble() 함수의 매개 변수 c로 복사되었다. 복사된 c의 size는 변경되었지만 main() 안의 circle1 객체는 변경되지 않았다. \\
 +이때 makedouble()의 매개 변수 c는 생성될 때 다른 객체의 내용을 복사하여서 생성된다. 따라서 일반적인 생성자가 호출되는 것이 아니라 ''복사 생성자'' 라는 특수한 생성자가 호출된다. 
 +이 방법은 함수에서 매개 변수를 변경하더라도 원본 객체에 영향이 없어 안전성이 높다는 장점이 있다. \\
 +
 +
 +====2.2. 참조자 매개 변수 사용하기====
 +<sxh cpp>
 +#include<iostream>
 +using namespace std;
 +
 +class circle
 +{
 +public:
 +    circle(int s) : size(s) {}
 +    int size;
 +};
 +
 +void makedouble(circle& c)
 +{
 +    c.size *= 2;
 +}
 +
 +int main()
 +{
 +    circle circle1(10);
 +    makedouble(circle1);
 +    cout << circle1.size << endl;
 +
 +    return 0;
 +}
 +</sxh>
 +결과 : 20\\
 +반면에 참조자를 통하여 객체를 변경하면 원래의 객체를 변경하는 것이다. 따라서 20이라는 결과가 나온다. \\
 +일반적으로 객체의 크기가 큰 경우 객체를 복사하는 시간이 오래 걸리기 때문에 객체의 참조자를 전달하는 편이 효율적이다. \\
 +
 +====2.3. 객체의 주소를 함수로 전달하기====
 +<sxh cpp>
 +#include<iostream>
 +using namespace std;
 +
 +class circle
 +{
 +public:
 +    circle(int s) : size(s) {}
 +    int size;
 +};
 +
 +void makedouble(circle *c)
 +{
 +    c->size *= 2;
 +}
 +
 +int main()
 +{
 +    circle circle1(10);
 +    makedouble(&circle1);
 +    cout << circle1.size << endl;
 +
 +    return 0;
 +}
 +</sxh>
 +결과 : 20\\
 +이 방법은 2.2.와 동일한 효과를 가진다. 
 +====2.4. 함수가 객체 반환하기====
 +<sxh cpp>
 +#include<iostream>
 +using namespace std;
 +
 +class Circle
 +{
 +public:
 +    Circle(int s) : size(s) { }
 +    int size;
 +};
 +
 +Circle createCircle()
 +{
 +    Circle c(10);
 +    return c;
 +}
 +
 +int main()
 +{
 +    Circle Circle1 = createCircle();
 +    cout << Circle1.size << endl;
 +
 +    return 0;
 +}
 +</sxh>
 +결과 : 10 \\
 +함수가 객체를 반환할 때도 객체의 내용이 복사될 뿐 원본은 전달되지 않는다. 이 경우 'createCircle()'은 함수 안에서 정의된 객체 변수 c를 메인함수의 circle1 로 복사한다. 
 +=====복사 생성자=====
 +이렇게 객체를 복사하여 객체를 생성할 때 사용하는 생성자인 복사생성자에 대해 알아보자. \\
 +1. 같은 종류의 객체로 초기화하는 경우
 +<sxh cpp>
 +Myclass obj(obj2); // 여기서 복사 생성자 호출
 +</sxh>
 +2. 객체를 함수에 전달하는 경우
 +<sxh cpp>
 +Myclass func(Myclass obj) // 여기서 복사 생성자 호출
 +{....
 +}
 +</sxh>
 +3. 함수가 객체를 반환하는 경우
 +<sxh cpp>
 +Myclass func(myclass obj)
 +{
 +    Myclass tmp;
 +    ...
 +    return tmp; // 여기서 복사 생성자 호출
 +}
 +</sxh>
 +이해하기 쉽도록 다음 예시를 살펴보자.
 +<sxh cpp>
 +#include<iostream>
 +using namespace std;
 +
 +class Person
 +{
 +public : 
 +    int age;
 +    Person(int a) : age(a) { }
 +};
 +
 +int main()
 +{
 +    Person A(21);
 +    Person clone(A); // 복사 생성자 호출
 +
 +    cout << "A의 나이 : " << A.age << ", clone의 나이 : " << clone.age << endl;
 +
 +    A.age = 25;
 +
 +    cout << "A의 나이 : " << A.age << ", clone의 나이 : " << clone.age << endl;
 +
 +    return 0;
 +}
 +</sxh>
 +
 +이 예시에서 A의 나이를 25로 바꿔도 clone은 A를 복사한 것이기 때문에 변화가 없다. 
 +
 +=====예제=====
 +Circle 클래스의 반지름을 교환하는 swap 함수를 작성해보자. 원본 객체를 받아서 실제로 객체의 내용이 교환되어야 한다. \\
 +
 +메인함수
 +<sxh cpp>
 +int main()
 +{
 +    Circle circle1(5);
 +    Circle circle2(3);
 +    
 +    cout << "circle1 넓이 : " << getArea(circle1) << ", circle2 넓이 : " << getArea(circle2) << endl;
 +
 +    swap(circle1, circle2);
 +
 +    cout << "circle1 넓이 : " << getArea(circle1) << ", circle2 넓이 : " << getArea(circle2) << endl;
 +
 +    return 0;
 +
 +</sxh>
 +출력 예시 :\\
 +circle1 넓이 : 78.5, circle2 넓이 : 28.26\\
 +circle1 넓이 : 28.26, circle2 넓이 : 78.5
 +=====3. 클래스 안에 객체 포함하기=====
 +객체 지향 프로그래밍에서는 하나의 객체 안에 다른 객체가 포함될 수 있다. 다음 예를 살펴보자.
 +<sxh cpp>
 +#include<iostream>
 +#include<string>
 +using namespace std;
 +
 +class Date
 +{
 +    int year, month, day;
 +public : 
 +    Date(int y, int m, int d) : year(y), month(m), day(d) { }
 +    void print()
 +    {
 +        cout << year << "." << month << "." << day << endl;
 +    }
 +};
 +
 +class Person
 +{
 +    string name;
 +    Date birth;
 +public : 
 +    Person(string n, Date d) : name(n), birth(d) { }
 +    void print()
 +    {
 +        cout << name << " : ";
 +        birth.print();
 +        cout << endl;
 +    }
 +};
 +
 +int main()
 +{
 +    Date d(2001, 6, 25);
 +    Person p("윤호연", d);
 +    p.print();
 +    return 0;
 +}
 +</sxh>
 +이 예시에서 Person 클래스 안에 Date 클래스를 포함하고 있는 것을 볼 수 있다. 
 +=====4. 정적 변수=====
 +정적 변수는 클래스안에서만 사용되는 전역 변수라고 생각하면 된다. 정적 변수는 앞에 ''static''을 붙여서 선언한다. 정적 변수의 초기화는 전역 변수와 비슷하게 클래스 외부에서 ''int Circle :: count = 0;'' 으로 수행한다. 
 +<sxh cpp>
 +class Circle
 +{
 +    int x, y;
 +    int radius;
 +    static int count;
 +
 +public : 
 +    Circle() : x(0), y(0), radius(0)
 +    {
 +        count++;
 +    }
 +    Circle(int x, int y, int r) : x(x), y(y), radius(r)
 +    {
 +        count++;
 +    }
 +};
 +int Circle :: count = 0;
 +</sxh>
 +=====5. 프렌드 함수와 프렌드 클래스=====
 +''프렌드''라는 메커니즘을 사용하면 외부의 클래스나 함수가 자신의 내부 데이터를 사용하도록 허가할 수 있다. 
 +<sxh cpp>
 +#include<iostream>
 +#include<string>
 +using namespace std;
 +
 +class A
 +{
 +public : 
 +    friend class B;
 +    A(string s = "") : secret(s) { }
 +
 +private : 
 +    string secret;
 +};
 +
 +class B
 +{
 +public : 
 +    B() { }
 +    void print(A obj)
 +    {
 +        cout << obj.secret << endl;
 +    }
 +};
 +
 +int main()
 +{
 +    A a("기밀 정보");
 +    B b;
 +    b.print(a);
 +
 +    return 0;
 +}
 +</sxh>
 +결과 : 기밀 정보\\
 +이처럼 A의 friend 클래스 B는 A의 private 멤버에도 접근 할 수 있다. 
 +\\
 +
 +=====6. 상속=====
 +====6.1. 상속이란?====
 +상속이란 기존에 존재하는 클래스로부터 모든 멤버를 이어받아 새로운 클래스를 만드는 것이다.\\
 +이미 존재하던 클래스를 기초 클래스 혹은 부모 클래스, 상위 클래스라고 하고 상속받는 클래스를 파생 클래스 또는 자식 클래스, 하위 클래스라고 한다.\\
 +다음은 상속에 사용되는 구문이다.
 +<sxh cpp>
 +class <자식 클래스> : <접근지정자> <부모 클래스> {
 +    · · ·    // 추가된 멤버 변수와 멤버 함수
 +}
 +</sxh>
 +대개 부모 클래스는 추상적이고 자식 클래스는 구체적이다.
 +^ 부모 클래스 ^ 자식 클래스 ^
 +| Animal | Dog, Cat, Tiger |
 +| Vehicle | Car, Bus, Truck, Boat, Bicycle |
 +| Shape | Rectangle, Triangle, Circle |
 +\\
 +부모 클래스의 멤버가 자식 클래스로 상속되어 자식 클래스는 부모 클래스의 멤버 변수와 멤버 함수를 자유롭게 사용할 수 있고, 필요하다면 자식 클래스만의 변수와 함수를 추가시킬 수도 있으며 이미 존재하는 멤부를 재정의할 수도 있다.\\
 +상속의 강점은 부모 클래스로부터 상속된 특징을 자식 클래스에서 추가, 교체, 상세화시킬 수 있다는 점이다.\\
 +
 +====6.2. 필요성====
 +상속을 사용하면 중복되는 코드를 줄일 수 있다.\\
 +Car, Bicycle 클래스는 공통적으로 speed, setSpeed(), getSpeed() 등의 멤버 변수와 멤버 함수를 갖는다.
 +이 클래스들을 개별적으로 작성하면 다음과 같다.
 +<sxh cpp>
 +class Car {
 +    int speed;
 +public:
 +    void setSpeed(int s) { speed = s; }
 +    int getSpeed() { return speed; }
 +};
 +
 +class Bicycle {
 +    int speed;
 +public:
 +    void setSpeed(int s) { speed = s; }
 +    int getSpeed() { return speed; }
 +};
 +</sxh>
 +Vehicle이라는 부모 클래스를 작성하여 상속을 사용하면 다음과 같다.
 +<sxh cpp>
 +class Vehicle {
 +    int speed;
 +public:
 +    void setSpeed(int s) { speed = s; }
 +    int getSpeed() { return speed; }
 +};
 +
 +class Car : public Vehicle { };
 +class Bicycle : public Vehicle { };
 +</sxh>
 +코드가 보다 간결해진 것을 볼 수 있다. 상속의 장점은 코드가 길어질수록 더 발휘된다.\\
 +중복되는 코드를 부모 클래스에 모으면 하나로 정리되어 관리하기나 유지 보수 및 변경이 수월해진다.\\
 +
 +====예제====
 +아래 코드가 올바르게 실행되도록 Shape 클래스를 상속받아 Rectangle 클래스 작성하기
 +  * Shape 클래스는 멤버 변수로 x, y를 갖고 있음
 +  * Shape 클래스는 멤버 함수로 printLocation()을 갖고 있음
 +  * Rectangle 클래스는 멤버 변수로 width와 height를 추가로 갖고 있음
 +  * Rectangle 클래스는 멤버 함수로 printArea()를 추가로 갖고 있음
 +  * 모든 멤버는 ''public''으로 지정하기
 +
 +<sxh cpp>
 +int main() {
 +    Rectangle r;
 +    r.x = 3;
 +    r.y = 6;
 +    r.width = 2;
 +    r.height = 8;
 +    r.printLocation();
 +    r.printArea();
 +
 +    return 0;
 +}
 +</sxh>
 +
 +  Location: (3, 6)
 +  Area: 16
 +
 +**예시 답안**
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Shape {
 +public:
 +    int x, y;
 +    void printLocation() {
 +        cout << "Location: (" << x << ", " << y << ")" << endl;
 +    }
 +};
 +
 +class Rectangle : public Shape {
 +public:
 +    int width, height;
 +    void printArea() {
 +        cout << "Area: " << width * height << endl;
 +    }
 +};
 +
 +int main() {
 +    Rectangle r;
 +    r.x = 3;
 +    r.y = 6;
 +    r.width = 2;
 +    r.height = 8;
 +    r.printLocation();
 +    r.printArea();
 +
 +    return 0;
 +}
 +</sxh>
 +====6.3. 상속과 생성자/소멸자====
 +상속된 자식 클래스의 객체가 생성될 때에는 부모 클래스의 생성자가 먼저 호출된 후 자식 클래스의 생성자가 호출된다.\\
 +특별히 지정하지 않으면 부모 클래스의 기본 생성자가 호출된다.\\
 +반대로 객체가 소멸될 때에는 자식 클래스의 소멸자가 먼저 호출된 뒤 부모 클래스의 소멸자가 호출된다.\\
 +다음 코드를 실행하여 생성자와 소멸자의 호출 순서를 확인해보자.
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Shape {
 +public:
 +    Shape() { cout << "Shape()" << endl; }
 +    ~Shape() { cout << "~Shape()" << endl; }
 +};
 +
 +class Circle : public Shape {
 +public:
 +    Circle() { cout << "Circle()" << endl; }
 +    ~Circle() { cout << "~Circle()" << endl; }
 +};
 +
 +int main()
 +{
 +    Circle c;
 +    return 0;
 +}
 +</sxh>
 +
 +  Shape()
 +  Circle()
 +  ~Circle()
 +  ~Shape()
 +\\
 +부모 클래스의 생성자를 지정하지 않으면 기본 생성자가 호출된다.\\
 +매개 변수가 있는 생성자를 호출하려면 자식 클래스의 생성자 헤더 뒤에 콜론을 추가하여 원하는 부모 클래스의 생성자를 적어주면 된다.
 +<sxh cpp>
 +<자식 클래스의 생성자>() : <부모 클래스의 생성자>() {
 +    · · ·
 +}
 +</sxh>
 +\\
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Shape {
 +    int x = 0, y = 0;
 +public:
 +    Shape() { cout << "Shape()" << endl; }
 +    Shape(int xloc, int yloc) : x(xloc), y(yloc) {
 +        cout << "Shape(" << xloc << ", " << yloc << ")" << endl;
 +    }
 +    ~Shape() { cout << "~Shape()" << endl; }
 +};
 +
 +class Circle : public Shape {
 +    int radius;
 +public:
 +    Circle(int x, int y, int r) : Shape(x, y), radius(r) {
 +        cout << "Circle(" << x << ", " << y << ", " << r << ")" << endl;
 +    }
 +    ~Circle() { cout << "~Circle()" << endl; }
 +};
 +
 +int main() {
 +    Circle c(0, 0, 1);
 +    return 0;
 +}
 +</sxh>
 +
 +  Shape(0, 0)
 +  Circle(0, 0, 1)
 +  ~Circle()
 +  ~Shape()
 +\\
 +
 +====예제====
 +Shape 클래스를 상속받아 Rectangle 클래스 작성하기
 +  * Shape 클래스는 멤버 변수로 x, y를 갖고 있음
 +  * Rectangle 클래스는 멤버 변수로 width와 height를 추가로 갖고 있음
 +  * 각각 생성자와 소멸자 작성하기
 +
 +<sxh cpp>
 +int main() {
 +    Rectangle(2, -4, 3, 5)
 +    return 0;
 +}
 +</sxh>
 +
 +  Shape(2, -4)
 +  Rectangle(2, -4, 3, 5)
 +  ~Rectangle()
 +  ~Shape()
 + 
 +**예시 답안**
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Shape {
 +private:
 +    int x, y;
 +public:
 +    Shape(int locX, int locY) : x(locX), y(locY) {
 +        cout << "Shape(" << x << ", " << y << ")" << endl;
 +    }
 +    ~Shape() { cout << "~Shape()" << endl; }
 +};
 +
 +class Rectangle : public Shape {
 +private:
 +    int width, height;
 +public:
 +    Rectangle(int locX, int locY, int w, int h) : Shape(locX, locY), width(w), height(h) {
 +        cout << "Rectangle(" << locX << ", " << locY << ", " << width << ", " << height << ")" << endl;
 +    }
 +    ~Rectangle () { cout << "~Rectangle()" << endl; }
 +};
 +
 +int main() {
 +    Rectangle r(2, -4, 3, 5);
 +    return 0;
 +}
 +</sxh>
 +====6.4. 접근 지정자====
 +클래스에서 멤버 변수들은 보통 ''private''으로 지정되어 외부의 접근을 막는다. 하지만 ''private''으로 지정되면 자식 클래스에서도 접근할 수 없다. 그렇다고 ''public''으로 지정하면 외부에서 접근할 수 있어 객체 지향에 어긋나게 된다.\\
 +이때 사용하는 접근 지정자가 바로 ''protected''이다.\\
 +
 +^ 접근 지정자 ^ 자기 클래스 ^ 자식 클래스 ^ 외부 ^
 +| private | ○ | ⨉ | ⨉ |
 +| protected | ○ | ○ | ⨉ |
 +| public | ○ | ○ | ○ |
 +\\
 +
 +====6.5. 멤버 함수 재정의====
 +멤버 함수 재정의란 멤버 함수의 헤더는 그대로 두고 몸체만을 교체하는 것이다.\\
 +멤버 함수의 이름, 반환형, 매개 변수의 개수와 자료형이 모두 일치해야 재정의가 일어난다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    void speak() {
 +        cout << "동물 소리" << endl;
 +    }
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() {
 +        cout << "멍멍" << endl;
 +    }
 +};
 +
 +int main() {
 +    Dog d;
 +    d.speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  멍멍
 +\\
 +위 코드에서 d.speak();을 실행했을 때 Animal 클래스의 speak() 함수가 아니라 Dog 클래스의 speak() 함수가 실행되었다.\\
 +Dog 객체를 통해 접근하면 Dog 클래스의 멤버 함수에 우선권이 있어 재정의된 함수가 실행된 것이다.\\
 +재정의된 함수가 아니라 부모 클래스의 함수를 실행하고 싶다면 범위 연산자 ''::''를 사용해 부모 클래스의 함수를 사용하겠다고 밝혀주면 된다.
 +
 +<sxh cpp>
 +int main() {
 +    Dog d;
 +    d.Animal::speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  동물 소리
 +\\
 +
 +<fs large>**중복 정의(overloading)와 재정의(overriding)의 차이**</fs>\\
 +\\
 +중복 정의는 같은 이름의 함수를 여러 개 정의하는 것이고 재정의는 상속받은 멤버 함수의 내용을 변경하는 것이다.\\
 +
 +====6.6. 상속 접근 지정자====
 +앞서 수행했던 상속은 모두 ''public''을 통해 이루어졌다. 하지만 접근 지정자에 세 가지가 있는 것처럼, ''public'' 외의 다른 두 가지 접근 지정자를 사용하여 상속할 수 있다.
 +
 +^ 상속 접근 지정자 ^ public ^ protected ^ private ^
 +| 부모 클래스의 public 멤버 -> | public | protected | private |
 +| 부모 클래스의 protected 멤버 -> | protected | protected | private |
 +| 부모 클래스의 private 멤버 -> | 접근 불가 | 접근 불가 | 접근 불가 |
 +
 +상속된 클래스의 멤버의 접근 수준이 상속 접근 지정자의 접근 수준보다 낮으면 해당 상속 접근 지정자로 덮어 씌워진다고 생각하면 된다.\\
 +상속 접근 지정자를 따로 지정하지 않을 경우 클래스에서 접근 지정자를 특별히 표기하지 않았을 시 ''private''으로 정해지는 것과 마찬가지로 상속에서도 기본값인 ''private''로 정해진다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace;
 +
 +class Base {
 +public: int a;
 +protected: int b;
 +private: int c;
 +};
 +
 +class Derived : Base {
 +    // a는 사용 가능, private
 +    // b는 사용 가능, private
 +    // c는 사용 불가
 +}
 +
 +int main() {
 +    Base baseObj;
 +    Derived derivedObj;
 +
 +    cout << baseObj.a;  // 접근 가능
 +    cout << baseObj.b;  // 접근 불가
 +    cout << baseObj.c;  // 접근 불가
 +    cout << derivedObj.a;  // 접근 불가
 +    cout << derivedObj.b;  // 접근 불가
 +    cout << derivedObj.c;  // 접근 불가
 +</sxh>
 +\\
 +
 +====6.7. 다중 상속====
 +다중 상속이란 말 그대로 두 개 이상의 부모 클래스로부터 상속받는 것을 뜻한다.
 +
 +<sxh cpp>
 +class Sub : public Sup1, public Sup2 {
 +    · · ·
 +};
 +</sxh>
 +
 +다중 상속을 사용할 때의 문제점이 하나 있는데, 바로 상속해주는 서로 다른 클래스에 똑같은 이름을 가진 멤버가 있을 경우 일반적인 접근으로는 해당 멤버를 사용할 수 없다는 것이다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Sup1 { public: int x = 1; };
 +class Sup2 { public: int x = 2; };
 +class Sub : public Sup1, public Sup2 { };
 +
 +int main() {
 +    Sub s;
 +    cout << s.x;  // 'Sub::x'가 모호합니다. 'x' 액세스가 모호합니다.
 +    return 0;
 +}
 +</sxh>
 +
 +이를 해결하려면 가리키는 대상이 모호하지 않게 해주면 된다.
 +
 +<sxh cpp>
 +int main() {
 +    Sub s;
 +    cout << s.Sup1::x;  // 1
 +    return 0;
 +}
 +</sxh>
 +\\
 +
 +
 +=====7. 다형성=====
 +====7.1. 다형성이란?====
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    void speak() {
 +        cout << "동물 소리" << endl;
 +    }
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() {
 +        cout << "멍멍" << endl;
 +    }
 +};
 +
 +class Cat : public Animal {
 +public:
 +    void speak() {
 +        cout << "야옹" << endl;
 +    }
 +};
 +
 +int main() {
 +    Dog d;
 +    Cat c;
 +    d.speak();
 +    c.speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  멍멍
 +  야옹
 +
 +위 코드를 보면 개와 고양이 객체에서 똑같은 speak() 함수를 실행시켰는데 서로 다른 결과를 얻었다.\\
 +이처럼 동일한 코드를 사용하지만 객체의 타입이 다르면 서로 다른 결과를 얻는 것이 다형성이다.\\
 +**<color #ed1c24>위 코드는 다형성의 개념을 설명하기 위한 예시일 뿐 실제 다형성이 아니다. 다형성은 포인터를 사용하여 수행된다.</color>**
 +====7.2. 상향 형변환====
 +다형성은 객체 포인터로 수행된다. Animal 클래스에서 상속받은 Dog 클래스를 생각해보자.
 +
 +<sxh cpp>
 +Animal* p = new Dog();
 +</sxh>
 +
 +Animal 타입의 포인터로 Dog 타입의 객체를 가리키는 이 코드는 놀랍게도 정상적으로 기능한다.\\
 +자식 객체는 부모 객체를 포함하고 있기 때문에 부모 포인터로 자식 객체를 가리킬 수 있는 것이다.\\
 +이를 상향 형변환이라고 한다.\\
 +다만, 부모 포인터로 가리키고 있기 때문에 이 방법으로는 자식 클래스 중에서 부모에게 상속받은 부분만을 사용할 수 있다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    void speak() {
 +        cout << "동물 소리" << endl;
 +    }
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() {
 +        cout << "멍멍" << endl;
 +    }
 +};
 +
 +int main() {
 +    Animal* p = new Dog();
 +    p->speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  동물 소리
 +
 +====7.3. 가상 함수====
 +위의 코드에서 Animal 포인터로 접근하더라도 객체의 종류에 따라 speak() 함수가 다르게 실행된다면 좋을 것이다. 이는 부모 클래스의 멤버 함수를 ''virtual'' 키워드를 통해 '가상 함수'로 정의하여 수행할 수 있다.
 +
 +<sxh cpp; highlight:[6]>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    virtual void speak() {
 +        cout << "동물 소리" << endl;
 +    }
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() {
 +        cout << "멍멍" << endl;
 +    }
 +};
 +
 +class Cat : public Animal {
 +public:
 +    void speak() {
 +        cout << "야옹" << endl;
 +    }
 +};
 +
 +int main() {
 +    Animal* p = new Dog();
 +    p->speak();
 +    p = new Cat();
 +    p->speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  멍멍
 +  야옹
 +Animal 형의 포인터로 서로 다른 형의 객체를 가리켰고, 똑같이 speak() 함수가 실행되었지만 서로 다른 결과를 얻었다. 이를 다형성이라 한다.
 +
 +====7.4. 참조자와 가상 함수====
 +참조자를 통해서도 다형성을 수행할 수 있다.\\
 +포인터를 사용하여 다형성을 수행할 때처럼 부모 클래스의 참조자로 자식 클래스를 참조할 수 있고 가상 함수 또한 기능한다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    virtual void speak() {
 +        cout << "동물 소리" << endl;
 +    }
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() {
 +        cout << "멍멍" << endl;
 +    }
 +};
 +
 +int main() {
 +    Dog d;
 +    Animal &a = d;
 +    a.speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  멍멍
 +\\
 +
 +====7.5. 가상 소멸자====
 +포인터로 다형성을 수행하고 나서 객체를 삭제해보자.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Sup {
 +public:
 +    ~Sup() { cout << "~Sup()" << endl; }
 +};
 +
 +class Sub : public Sup {
 +public:
 +    ~Sub() { cout << "~Sub()" << endl; }
 +};
 +
 +int main() {
 +    Sup* p = new Sub();
 +    delete p;
 +    return 0;
 +}
 +</sxh>
 +
 +  ~Sup()
 +
 +자식 객체를 생성하여 부모 포인터로 가리킨 후 객체를 삭제하면 자식 소멸자가 호출되지 않고 부모 소멸자만 호출된다.\\
 +자식 소멸자도 호출되게 하려면 부모 클래스의 소멸자를 가상 함수로 선언하면 된다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Sup {
 +public:
 +    virtual ~Sup() { cout << "~Sup()" << endl; }
 +};
 +
 +class Sub : public Sup {
 +public:
 +    ~Sub() { cout << "~Sub()" << endl; }
 +};
 +
 +int main() {
 +    Sup* p = new Sub();
 +    delete p;
 +    return 0;
 +}
 +</sxh>
 +
 +  ~Sub()
 +  ~Sup()
 +\\
 +
 +====7.6. 순수 가상 함수====
 +순수 가상 함수는 함수의 정의가 없고 선언만 된 함수이다.
 +
 +<sxh cpp>
 +virtual <반환형> <함수명>(매개 변수) = 0;
 +</sxh>
 +
 +순수 가상 함수를 갖고 있는 클래스를 추상 클래스라고 한다.\\
 +추상 클래스는 추상적인 개념을 나타내거나 클래스 간 인터페이스를 나타내는 용도로 사용된다.\\
 +추상 클래스로는 객체를 만들 수 없고 상속으로만 사용된다.\\
 +또, 함수 정의가 없기에 상속받은 자식 클래스는 순수 가상 함수를 재정의해야 한다.
 +
 +<sxh cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class Animal {
 +public:
 +    virtual void speak() = 0;
 +};
 +
 +class Dog : public Animal {
 +public:
 +    void speak() { cout << "멍멍" << endl; }
 +};
 +
 +class Cat : public Animal {
 +public:
 +    void speak() { cout << "야옹" << endl; }
 +};
 +
 +int main() {
 +    Animal* p = new Dog();
 +    p->speak();
 +    p = new Cat();
 +    p->speak();
 +    return 0;
 +}
 +</sxh>
 +
 +  멍멍
 +  야옹
 +
 +추상 클래스를 사용하지 않고 각 클래스별로 멤버 함수를 정의하여 사용할 수도 있다.\\
 +하지만 그럴 경우 데이터가 커지게 되면 몇 가지 공통 요소를 빠트릴 수 있다.\\
 +추상 클래스를 사용하게 되면 함수를 꼭 재정의해야 하기 때문에 이러한 실수를 미연에 방지할 수 있다.\\
 +또, 추상 클래스를 정의할 때에는 멤버 변수 없이 온전히 순수 가상 함수로만 이뤄져야 좋다.\\
 +
 +====예제====
 +Shape 클래스를 상속받아 Circle, Rectangle, Triangle 클래스 작성하여 다형성 수행하기
 +  * Shape 클래스는 멤버 변수로 x, y를 갖고 있음
 +  * Shape 클래스는 가상 함수로 getArea()를 갖고 있음
 +  * 아래 본문을 실행하여 올바른 결과가 나오도록 클래스 부분 작성하기
 +
 +<sxh cpp>
 +int main() {
 +    Shape* s = new Circle(0, 4, 5);
 +    cout << "Circle : " << s->getArea() << endl;
 +    s = new Rectangle(3, -2, 6, 4);
 +    cout << "Rectangle : " << s->getArea() << endl;
 +    s = new Triangle(2, 5, 7, 3);
 +    cout << "Triangle : " << s->getArea() << endl;
 +
 +    return 0;
 +}
 +</sxh>
 +
 +  Circle : 78.5398
 +  Rectangle : 24
 +  Triangle : 10.5
 +
 +**예시 답안**
 +<sxh cpp>
 +#define _USE_MATH_DEFINES
 +#include <iostream>
 +#include <cmath>
 +using namespace std;
 +
 +class Shape {
 +private:
 +    int x, y;
 +public:
 +    Shape(int locX, int locY) : x(locX), y(locY) {}
 +    virtual double getArea() = 0;
 +};
 +
 +class Circle : public Shape {
 +private:
 +    int radius;
 +public:
 +    Circle(int locX, int locY, int r) : Shape(locX, locY), radius(r) {}
 +    double getArea() { return radius * radius * M_PI; }
 +};
 +
 +class Rectangle : public Shape {
 +private:
 +    int width, height;
 +public:
 +    Rectangle(int locX, int locY, int w, int h) : Shape(locX, locY), width(w), height(h) {}
 +    double getArea() { return width * height; }
 +};
 +
 +class Triangle : public Rectangle {
 +public:
 +    Triangle(int locX, int locY, int w, int h) : Rectangle(locX, locY, w, h) {}
 +    double getArea() { return Rectangle::getArea() / 2; }
 +};
 +
 +int main() {
 +    Shape* s = new Circle(0, 4, 5);
 +    cout << "Circle : " << s->getArea() << endl;
 +    s = new Rectangle(3, -2, 6, 4);
 +    cout << "Rectangle : " << s->getArea() << endl;
 +    s = new Triangle(2, 5, 7, 3);
 +    cout << "Triangle : " << s->getArea() << endl;
 +
 +    return 0;
 +}
 +</sxh>