관리 메뉴

Kim's Programming

C++ - 구조체의 확장 본문

Programming/Cplusplus

C++ - 구조체의 확장

Programmer. 2015. 8. 27. 13:41

조체의 확장


멤버함수


구조체는 타입이 서로 다른 이형 변수의 집합입니다. 화면상의 한 좌표와 그 위치에 출력될 문자에 대한 정보를 저장하고 싶다면 다음과 같은 구조체를 선언해야합니다. 좌표값 (x, y)는 정수형이고 문자 ch는 문자형이기 떄문에 서로 타입이 다르고 이형 타입 변수의 집합인 구조체로 묶어서 정의합니다.
1
2
3
4
5
6
struct Position
{
    int x;
    int y;
    char ch;
}
cs
타입의 이름은 Position이고 이 구조체 안에 x, y, ch 멤버가 포함되어 있습니다. 다음 예제는 이 구조체를 사용하여 화면에 한 문자를 출력합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
 
struct Position
{
    int x;
    int y;
    char ch;
};
 
void OutPosition(Position Pos)
{
    printf("x좌표는 %d, y좌표는 %d입니다.\n", Pos.x, Pos.y);
    printf("입력된 문자는 %c\n", Pos.ch);
}
 
void main()
{
    Position Posit;
    Posit.x = 30;
    Posit.y = 60;
    Posit.ch = 'A';
    OutPosition(Posit);
}
cs
실행해 보면 입력된 값이 출력이 됩니다. 문자를 출력하는 작업은 OutPosition함수가 처리하는데 인수로 전달받은 구조체 Posit의 정보대로 지정한 값을 출력합니다. main 함수는 Position형의 구조체 변수 Posit을 선언하고 이 구조체 멤버에 30, 60, 'A'를 대입했으며 OutPosition 함수를 호출하여 x좌표는 30, y좌표는 60입니다. 라고 출력합니다. 여기서는 Position구조체와 OutPosition 함수는 상호 읜존적인 관계에 있습니다. Position구조체가 없으면 OutPosition은 인수를 받아들일 수 없으므로 컴파일 되지 않았으며 동작할 수도 없습니다. 또한 OutPosition 은 인수를 받아들일 수 없으면 컴파일도 안되고 동작할 수도 없습니다. 또한 OutPosition함수가 없으면 Position 구조체는 화면에 출력되지 못하므로 자신의 존재를 나타낼 방법이 없습니다. Position은 정보를 가지고 OutPosition은 동작을 정의하는 정보를 보여주기 위해서 동작이 필요하고 동작이 수행되려면 정보가 필요한것입니다.

만약 이 구조체를 다른 프로그램에서 재사용 하고자 한다면 한다면 구조체와 함수를 같이 가지고 가야하며 둘 중 하나만 가지고 가면 둘 다 쓸모가 없습니다. 이렇게 밀접한 구조체와 함수는 한 쌍으로 볼 수 있는데 C++은 연관된 코드와 데이터를 하나의 범위에 포함시킬 수 있는 방법을 제공합니다. 이 개념은 바로 캡슐화이며 구조체가 함수도 포함할 수 있습니다. 다음 나오는 소스는 Position구조체와 OutPosition 함수를 합치는 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
 
struct Position
{
    int x;
    int y;
    char ch;
    void OutPosition()
    {
        printf("x좌표는 %d, y좌표는 %d입니다.\n", x, y);
        printf("입력된 문자는 %c\n", ch);
    }
};
 
 
void main()
{
    Position Posit;
    Posit.x = 30;
    Posit.y = 60;
    Posit.ch = 'A';
    Posit.OutPosition();
}
cs
실행의 결과는 위에 것과 동일합니다. 변화점은 OutPosition 함수를 구조체 안에 포함시켜 버린것입니다. 이런 함수를 멤버 함수라고 하고 변수는 멤버 변수라고 합니다. C++에서 구조체는 멤버 변수와 멤버 함수로 구성됩니다. C의 구조체는 이형 변수의 집합으로 변수만 포한될 수 있었지만 C++의 구조체는 함수도 같이 포함할 수 있어서 구별하는 이름을 가지고 있습니다. 위 처럼 소스가 변경된 점을 알아 보겠습니다.

    • OutPosition 함수가 인수를 받아들일 필요가 없습니다. 일반 함수일 때는 어떤 구조체의 정보를 사용할 것인지 인수로 전달받아야 했지만 구조체에 소속되었기 때문에 소속된 구조체의 정보를 사용하면 됩니다.

    • OutPosition함수 내부에서 x,y 멤버 변수를 참조할 때 소속 구조체를 밝힐 필요가 없어졌습니다. 구조체 박에 있을 떄 어떤 구조체에 속한 멤버 변수인지를 밝혀야 하지만 멤버 함수는 별도의 지정없이 자신이 속해있는 구조체의 멤버 변수를 이름만으로 액세스할 수 있습니다.

    • main이 OutPosition 함수를 호출할 떄 함수가 소속된 구조체 변수 Posit을 앞에 적어주었습니다. OutPosition 함수는 독립된 함수가 아니라 구조체에 속한 멤버이므로 어떤 구조체의 정보를 대상으로 동작할 것인지를 밝여야 합니다. 멤버 함수를 호출하는 방법은 멤버 변수를 참조하는 것과 동일합니다. 점 연산자를 사용하여 구조체.함수()식으로 호출하며 포인터라면 구조체->함수() 식으로 호출합니다.

멤버 함수 작성

위의 소스에서는 구조체 안에 함수의 전문을 넣었는데 만약에 함수의 코드가 길기도 하며 멤버 함수가 많으면 코드 관리가 어려워 집니다. 그래서 C++은 구조체 선언문에 원형만 선언하고 구조체 바깥에 따로 작성하는 방법을 지원합니다. 다음은 함수 본체를 외부에 외부에 따로 작성한 경우입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdio.h>
 
struct Position
{
    int x;
    int y;
    char ch;
    void OutPosition();
    
};
 
void Position::OutPosition()
{
    printf("x좌표는 %d, y좌표는 %d입니다.\n", x, y);
    printf("입력된 문자는 %c\n", ch);
}
void main()
{
    Position Posit;
    Posit.x = 30;
    Posit.y = 60;
    Posit.ch = 'A';
    Posit.OutPosition();
}
cs

구조체 선언문 안에는 함수의 원형만 밝혀 이 함수가 Position 구조체 소속임을 밝힙니다. 함수의 본체는 구조체 선언문 다음에 별도로 작성하되 함수 정의문에 이 함수가 어떤 구조체의 멤버 함수인지를 밝혀야 합니다. 함수 이름 앞에 소속 구조체를 쓰고 그 사이에 범위 연산자 ::를 넣어 구분합니다. 멤버 함수를 정의하는 기본 문법은 다음과 같습니다.


리턴타입 소속 구조체::멤버함수(인수)

{

본체

}

일반 함수 선언문과 비슷하되 함수명 앞에 "소속구조체::"이 추가된다는 점만 다릅니다. Void Position::OutPosition()은 Position 구조체에 속한 OutPosition 구조체에 속한 OutPosition 멤버 함수라는 뜻입니다. 함수의 본체가 구조체 선언 내부에 있건 외부에 있건 이 함수가 구조체의 포함된다는 사실에는 변함이 없습니다. 다만 본체 정의 위치에 따라 멤버 함수가 호출되는 방법상의 차이가

    • 내부 정의 : 인라인 속성을 가집니다. 실제로 함수가 호출되는 것이 아니라 멤버 함수를 호출하는 코드가 함수의 본체코드로 대체됩니다.

    • 외부 정의 : 일반적인 함수 호출과 마찬가지 방법으로 멤버 함수를 호출합니다. 스택을 경유하고 인수를 넘기고 제어의 분기가 발생합니다.

인라인 함수는 호출 부담이 없기 때문에 속도가 빠르지만 여러 번 호출할 경우 실행 파일의 크기를 증가시키는 단점이 있습니다. 멤버 함수의 코드가 아주 짧을 때 예를 들어 멤버변수의 값을 읽기만 한다거나 단순한 연산문일 때는 내부 정의를 하고 그렇지 않을 때는 외부 정의하는 것이 좋습니다. 절대적인 기준은 없지만 보통은 3줄 이하일때는 내부 정의를 하는 것이 보통입니다. 멤버 함수를 외부에 정의하면서도 인라인으로 만들고 싶다면 inline 키워드를 함수 원형 앞이나 정의부 앞에 밝히면 됩니다. 물론 둘 다 써줘도 상관은 없습니다.

멤버함수는 클래스 내부에 정의하면 이 함수는 무조건 인라인 속성을 가집니다. 외부 정의하면서 인라인으로 지정하는 문법은 존재하지만 내부 정의하면서 일반 함수로 만드는 문법은 없습니다. 클래스 선언은 어디까지나 클래스의 모양을 알리는 것 뿐 실제코드는 생성되지 않습니다.

엑세스 지정


구제체의 멤버는 외부에서 언제든지 참조할 수 있습니다. 앞 소스에서처럼 main에서 posit.x를 읽을 수 있고 새로운 값 대입도 되고 Posit의 멤버함수 OutPosition을 호출하여 출력 동작도 할 수 있었습니다. 구조체에의 멤버 뿐만 아니라 선언되어있는 변수를 참조하거나 작성되어 있는 함수를 호출 할 수 있는것은 C 수준에서는 당연한 이야기힙니다. 하지만 구조체 내부의 멤버를 외부에서 마음대로 건드리도록 내버려 두면 부주의한 사용으로 버그가 발생할 수 있어 위험할 뿐만아니라 객체의 독립성도 떨어집니다. 그래서 C++에서는 구조체(또는 클래스)의 멤버에 대한 외부 참조를 허가할것인지 금지할 것인지를 지정할 수 있으며 이것도 액세스 지정이라고 하고 세가지 종류가 있습니다.


    • private : 이 속성을 가지는 멤버는 외부에서 액세스할 수 없으며 구조체의 멤버 함수만 액세스할 수 있습니다. 외부에서는 프라이빗 멤버를 읽을 수 없음은 물론이고 존재도 알려지지 않습니다.

    • public : 이 속성을 가지는 멤버는 외부로 공개되어 어디서나 쓸 수 있고 함수의 경우는 호출할 수 있습니다. 구조체가 자신의 속성이나 동작을 외부로 공개하는 수단이 되며 퍼블릭 멤버를 인터페이스라고 합니다.

    • protected : 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
#include<iostream>
 
struct str
{
private:
    int a;
    double b;
    char ch;
    void Initialize();
public:
    int x;
    int y;
    void function(int x);
protected:
    float k;
};
 
void main()
{
    str S;
    S.a = 1;//에러
    S.x = 10;//대입 가능
    S.function(3);//호출 가능
    S.Initialize();//에러
}
cs

액세스 지정자의 순서에 대한 제약은 없습니다. 필요에 따라 중복도 됩니다. 단지 통상적으로 private: protected: public:순으로 선언합니다. 액세스 지정자를 사용하여 특정 멤버를 숨실 수 있는 OOP의 기능을 정보 은폐라고 합니다. 선언된 멤버를 외부에서 엑세스하지 못하도록 숨기면 어떤 효과가 날까요? 정보 은폐는 객체의 안전성을 확보하고 독립성을 높히게 되는데 이는 뒤에서 포스팅하겠습니다.