본문 바로가기
C++/기초(두들낙서)

[C++] 순수가상함수와 추상클래스

by Meaning_ 2022. 7. 12.
728x90
반응형

순수가상함수?

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A {
 
public:
    virtual void f(){
        cout << "A::f()" << endl;
    }
};
 
class B :public A {
 
public:
    void f(){
        cout << "B::f()" << endl;
    }
};
 
 
int main() {
 
    A* a = new A;
    a->f();
    delete a;
 
    
}
cs

이런 상태에서 a의 가상함수인 f를 호출한다면 a에 있는 f함수가 호출된다.

근데 순수 가상함수는 아무것도 호출되지 않는 함수이다.

virtual void f()=0; 같은 애를 순수가상함수라고 한다.

이렇게 되면

 

인스턴스를 생성할수 없다.

그리고 그런 클래스를 추상클래스라고 한다.

 

대신 상속을 통해 오버라이딩하면 자식클래스는 객체를 만들수 있다. 

 

++)  한번 가상함수로 선언된 함수는 따로 virtual 키워드를 앞에 써주지 않아도 가상함수로 인식합니다.  

 

그렇다면 순수가상함수를 왜 쓰는 것일까?

 

가상함수의 허점은 부모 클래스에서 구현한 가상함수를 반드시 자식클래스가 구현해야한다는 강제성이 없다.이러면 클래스 상속을 통해서 오버라이딩을 안하게 되기에 다형성을 구성하지 못한다. 근데 순수가상함수를

쓰면  해당 클래스에서는 구현할 수 없고 상속받은 자식클래스에서 반드시 재정의 해야하기 때문에 다형성을 구성할 수 있다.

 

https://velog.io/@hidaehyunlee/CPP-04-ex01-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1-%EC%88%9C%EC%88%98-%EA%B0%80%EC%83%81%ED%95%A8%EC%88%98

 

[CPP-04 / ex01] 추상 클래스의 필요성: 순수 가상함수

추상 클래스의 특징은 오버라이드, 즉 '함수를 반드시 재정의해야하는 것'이라고 정리할 수 있을 것 같다. 순수 가상함수를 재정의하지 않으면 코드 상에서 에러로 판단하기 때문에 실수들을 방

velog.io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Shape {
 
public:
 
    virtual double GetArea() = 0;
 
    virtual void Resize(double f) = 0;
 
    
 
 
 
};
 
class Circle :public Shape{
 
 
public:
    Circle(double r):r(r){}
 
    double GetArea() {
        return 3.14 * r * r;
    }
 
    void Resize(double f) {
        r *= f;
    }
 
private:
    double r;
};
 
class Rectangle:public Shape {
 
public:
    Rectangle(double a,double b):a(a),b(b){}
 
    double GetArea() {
        return a * b;
    }
 
    void Resize(double f) {
        a *= f;
        b *= f;
    }
 
private:
    double a, b;
};
 
 
int main() {
 
    //포인터 배열로 만들어줌
    Shape *shapes[] = {
 
        new Circle(10),
        new Rectangle(20,30),
    };
 
    for (Shape* s : shapes) {
        s->Resize(2);
    }
 
    for (Shape * s : shapes) {
        cout << s->GetArea() << endl;
 
    }
 
    for (Shape* s : shapes) {
        delete s;
    }
    
}
cs

 

위의 예시처럼 추상클래스는 부모클래스로 부터  무조건 받아와야 하는 함수가 있을 때 (Resize,GetArea같은)

그것을 강제하기에 유용한 역할을 한다. 

 

그리고 클래스 포인터배열을 선언해줌으로써 주소값을 배열에 담는다. 

그냥 배열에 담게 되면 그 클래스로 부터 상속받은 자식클래스의 멤버에는 접근 할 수 없다. 

 

그리고 동적할당 했기 때문에 delete 해주는 것도 잊지 않아야한다. 

 

++) 중요!

 

가상함수를 오버라이딩 했다면 앞에 virtual을 쓰지 않았더라도 virtual이 숨겨져 있는 것

가상함수에서는 실제로 가리키는게 중요하다.

예를 들어 A가 부모클래스이고, B가 자식클래스이며 두 클래스 모두 f라는 가상함수가 오버라이딩 되어있다고 해보자. 

 

A *a=new B; 일때 a라는 포인터 변수가 실제로 가리키는게 B 클래스이기에

a->f()라면 

B 클래스의 f함수를 호출한다. 실제 객체의 타입이 A더라도 가상함수에서는 실제로 가리키는 B를 바탕으로 작동한다. 

 

728x90
반응형

댓글