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

[C++기초] 상속과 접근제어 ,오버라이딩

by Meaning_ 2022. 1. 17.
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
26
27
28
29
30
31
32
33
34
35
36
#include<iostream>
 
using namespace std;
 
class Base {
public:
    void bFunc() {
        cout << "Hello" << endl;
    }
 
    int bNum;
};
 
class Derived : public Base {
 
public:
    void dFunc() {
        cout << "Hello?" << endl;
    }
    int dNum;
};
 
int main() {
    Base b;//부모 클래스
    Derived d; //자식 클래스
 
    b.bFunc();
    b.bNum = 1;
 
    //d에는 b의 모든 멤버와 d의 멤버도 접근가능
 
    d.bFunc();
    d.dFunc();
    d.bNum = 2;
    d.dNum = 1;
}
cs

 

d객체는 bFunc,bNum에도 접근이 가능하다.

 

접근제어

상속을 할때 쓰이는 접근제어 지시자는 protected가 있다. 

 

public ->외부 접근 허용 , 자식클래스에서 접근 허용

protected -> 외부 접근 비허용 ,자식클래스에서 접근 허용 

private ->외부접근 비허용, 자식클래스에서 접근 비허용

 

class Derived : public Base 에서 public의 의미는 public으로 Base를 상속받아온다는 것이다.

 

만약 class Derived: protected Base 였다면 protected로 Base를 상속받아오는 것이여서

Base에서 public이였던 애도 protected로 상속받아오게 된다. (protected->protected ,private->원래대로 접근불가능)

 

 

오버라이딩 

override는 '우선하다'라는 의미를 가지고 있다. 

 

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
#include <iostream>
 
using namespace std;
 
//override:우선하다 
 
class Base {
 
public:
    int a = 10;
};
 
class Derived :public Base {
public:
    int a = 20;
};
 
int main() {
 
    Base b;
    Derived d;
 
    cout << b.a << endl;
    cout << d.a << endl;
    
}
cs

 

Derived라는 클래스를 잘 보면 Derived가 고유로 가지고 있는 a도 있지만 Base에서 상속받아온 a도 있다.

상속을 받아왔을 때 이름이 같다면 자기 고유의 것을 먼저 출력한다.그렇기에 d.a를 하면 Dervied의 고유의 값 a에 해당하는 20이 출력된다.  이것을 오버라이딩이 하고, "우선한다"는 것과 동치되는 말이기도 하다. 

Derived에서 Base에 접근하고 싶다면 

d.Base::a를 해주면 된다.

 

10이 출력(상속받아온 값) 되는 것을 알 수 있다.

 

상속관련 예제 

 

메신저 프로그램을 만들건데, text와 이미지 변수로 private 필드를 구성한 프로그램을 만들어보자

 

 

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <string>
 
using namespace std;
 
//메신저
//텍스트,이미지 주고받는 메신저 구현
 
 
class Image {
 
public:
    operator string() {
        //string으로 형변환 
 
        return "사진";
    }
};
 
class Message {
 
public:
    Message(int sendTime, string sendName) {
        this->sendTime = sendTime;
        this->sendName = sendName;
 
    }
    int GetSendTime()const {
        return sendTime;
    }
 
    string GetSendName()const {
        return sendName;
    }
private:
    int sendTime;
    string sendName;
 
};
 
class TextMessage:public Message {
public:
    TextMessage(int sendTime, string sendName, string text) 
        :Message(sendTime, sendName) {
            //부모클래스의 생성자 먼저 실행
            this->text = text;
        }
        
 
    string getText()const {
        return text;
    }
    
private:
 
    string text;
};
 
class ImageMessage :public Message{
 
    //이미지 자체를 동적할당할 수 있지만 이미지 안에 클래스에서 동적할당 된게 없기 때문에
    //포인터 얕은 복사는 의미 없음.
public:
    ImageMessage(int sendTime, string sendName,Image *image) 
        :Message(sendTime,sendName){
                this->image = image;
 
    }
 
    
    Image* GetImage()const {
        return image;
    }
 
private:
    int sendTime;
    string sendName;
    
    Image* image;//이미지는 용량이 크기 때문에 이미지에 대한 포인터값만 저장
 
};
 
 
int main() {
 
    Image* p_dogImage = new Image(); //동적할당
 
    TextMessage* hello = new TextMessage(10"두들""안녕");
    ImageMessage* dog = new ImageMessage(20,"두들", p_dogImage);
 
    cout << "보낸시간: " << hello->GetSendTime() << endl;
    cout << "보낸 사람: " << hello->GetSendName() << endl;
    cout << "내용:" << hello->getText() << endl;
    cout << endl;
    cout << "보낸시간: " << dog->GetSendTime() << endl;
    cout << "보낸 사람: " << dog->GetSendName() << endl;
    cout << "내용:" << (string)*dog->GetImage() << endl;
 
    delete hello;
    delete dog;
    delete p_dogImage;
}
 
cs

 

 

이 코드에서 중요한건 세가지이다.

 

1. 상속을 이용해 Message 부모 클래스를 만들고  -> TextMessage, ImageMessage 로 자식클래스를 만들었다. 

자식클래스인 TextMessage를 잘 보면 생성자 위임 처럼 보이는 Message(sendTime,sendName)이 있다. 얘를 상속받아 온거기 때문에 부모 클래스의 생성자가 먼저 실행되고 -> 자식 클래스의 생성자가 실행된다. 

 

2. 부모클래스에서는 protected 보단 생성자를 이용해주자!

 

 

부모클래스와 자식클래스에는 공통적으로 sendTume과 sendName이 들어간다. 이럴떈 부모클래스의 protectd필드에

이 두변수를 선언해주기 보다는 그냥 부모 클래스 생성자에 sendTime과 sendName이 매개변수로 들어가는 생성자를

만들어줘서 자식클래스가 부모클래스의 생성자를 상속받을 수 있게 한다.

내 느낌에는 원래 부모 클래스의 private 필드에 있는 sendTIme이나 sendName이 변질되면 안되는데

protected로 선언되면 변질이 될 수 있기에 그냥 얘는 private로 유지시켜주고 생성자만 상속시켜주는 것 같다. 

 

 

그리고 this->sendTime안하고 sendTime하면 매개변수랑 똑같이 인식하기 때문에 당연히 에러가 뜬다.

this포인터를 이용해 private 필드에 있는 나 자신의 sendTime에 접근해주는 거다.

 

 

[TMI정보들]

 

1) (string)*dog->GetImage() 의 의미는 무엇일까?

main함수에 독특한 코드가 있다. 이건 dog->GetImage를 하면 dog가 포인터이기 때문에 (동적할당 하느라)

 주소값이 반환된다. 그래서 * 해주면서 얘가 반환된 값을 가리키게 하고 (string)은  맨 위에 Image 클래스에

string 형변환 메서드를 통해 string 형변환이 되게 해주는 것이다. 

 

 

2) ImageMessage에서 얕은복사 해줘도 될까? 

 

this->image=image;를 하면 얕은 복사가 될것이다. 

이미지 자체를 동적할당 할수 있기는 하다. 그러나 이미지 클래스 자체를 보자.

 여긴 동적할당 된 메모리가 없다. 그래서 포인터 얕은 복사 해줘도 아무 문제 없다. 

 

3) 생성자에서 this포인터 쓰는 이유?

this->sendTime을 해주면서 멤버변수의 sendTime에 접근한다.(멤버변수와 매개변수명이 같아서 둘을 구분하기 위해 this쓰는것!)

728x90
반응형

댓글