출처: https://m.blog.naver.com/justkukaro/220767142003
[[목차]]
1.모듈화란?
2.static 전역변수
3.소스파일의 분할
4.헤더파일의 사용법
5.opaque 포인터
6.객체지향 기법
7.마치며
1.모듈화란?
C로 프로그램을 입문하게 처음에는 모듈화라는 개념없이 주구장창 코드를 길게쓰면됩니다.
왜냐하면 왠만한 책의 문제들이나 과제들은 줄이 아무리 길어져봐야 300줄도 넘기기 힘듭니다.
그러나 자그마한 게임을 만들다던가 혹은 조금 큰 프로그램을 만들다면 코드의 길이가 눈에 띄게 길어집니다.
이러면 모듈화 하지 않은 프로그램은 읽기가 정말 난해합니다.
특히 전역변수를 설정해서 이렇게 저렇게 막 프로그래밍을 했다면 수정하는 입장에서는
한가지를 고치기위해서 프로그램을 뜯어고치려는 대격변을 거쳐야합니다.
그렇기때문에 C언어에서는 코드의 분할을 굉장히 중요시합니다.
코드를 쪼개고 쪼개고 또 쪼개며 계층화를 하고 각각의 계층의 문제는 그 코드 내에서 해결하게 하는 식이죠.
이러한 방식을 구조적 프로그래밍이라고합니다.
모듈화의 가장 중요한 점은 사람이 생각하기에 논리적으로 같은 등급의 소스파일은 계속해서 분할하는 겁니다.
그리고 두번째 중요한것은 어떠한 소스파일에서, 혹은 어떠한 함수에서 일어나는 일의 책이임을
그 소스파일과 그 함수가 지도록하는 것이죠. 이렇게 조심해서 프로그래밍 하는것을 캡슐화 혹은 은닉화라합니다.
2.전역 static
static 전역변수의 특징은 과거 C언어 10장 변수파트에서 말씀 드린적이 있습니다.
이 static 전역변수는 다른게 중요한게 아니라 같은 이름의 변수를 중복선언할 수 있다고 했습니다.
그렇다면 다시 말해서 이 static 전역 변수의 경우 중요한 역활을 맡게할 수 있는데,
단 하나밖에 필요가 없으면서 또한 다른 소스파일에도 쓰일법한 이름의 키워드를 중복설정할 수 있다는 것입니다.
가장 기초적으로 count나 num, data같은 이름은 흔합니다. 만약 충돌을 피하고싶다면
기본적인 방법으로는 소스파일_변수명 같은 형식으로 지정해주면됩니다만..
확실한 방법이긴하지만 조금 피곤한 방법이기도 합니다. 게다가 설사 그러한 변수라할지라도 충돌될 위험이있을 경우,
미연에 방지해주는 역활을 동시에 할 수 있습니다.
따라서 소스파일이 중복될 가능성이 있으면서 다른 소스파일에 사용을 허가하고싶지 않은 변수를
static화 시켜줄 수 있습니다.
큰내용이니까 사진을 확대해주시면서 보시는게 도움이 될겁니다.
총 세개의 소스파일이 존재합니다. 하나는 main으로 실질적인 동작부인 main소스와 랜덤한 배열을 가지는 RandNumArr소스와
어떠한 배열을 정렬시켜주는 SortArr소스가 존재합니다.
여기서 주의 깊게 봐야할부분은 static이 붙어있는 모든 변수와 함수입니다.
static은 각각 그 소스파일에 귀속시켜서 다른 소스파일에서 쓰지못하게 하는 효과를 지녔습니다.
그러므로 네임스페이스(이름 사용 공간)이 고정이 되므로 중복선언이 가능해집니다.
RandNumArr.c와 SortArr.c의 count가 완전히 다른역활로 각각 소스파일에 존재하지만 에러가나지않고 잘 작동되죠.
이는 이미 과거 10장에서 배운 개념입니다.
단 이렇게 귀속화된 변수및 함수는 직접적으로 다른 소스파일에서는 접근할 방법이 없으므로 우회해야합니다.
예를들어 GetRandArr함수가 존재하는 이유는 RandNumArr소스의 Arr배열은 static이 걸려서 외부에서 사용할 수 없으므로,
이의 주소를 리턴해주는 함수가 필요합니다. 이 원리는 C++이나 자바,C#등에서 사용되는 접근자와 같은 원리입니다.
여기서 중요한건 외부에 알릴이유가 없으면서 또한 중복될 가능성이 있는 모든 변수와 함수에 static을 걸어주는것이죠.
함수와 변수를 나눠서 외부에 알릴 필요가 있다면 statc을 해제하고 알릴 필요가 없다면 static을 반드시 걸어줍니다.
이렇게 나누는 방식은 일종의 객체지향방식과 흡사한데요, 사실 객체지향방식의 전신이 되는 방식이기도합니다.
3.소스파일의 분할
가장 중요한 부분중하나인데요, 모듈화 프로그래밍에서는 모든 소스파일의 의미가있는 논리집단으로 분리를 해줘야합니다.
프로그램이 커지면 커질수록 관리하기는 더더욱 어려우므로 각각의 소스파일을 쪼개고 쪼개여서 관리해주면 더 편하죠.
특히 계층화를 시켜줄수록 더더욱 편합니다. 그럼 이때까지보단 조금 큰 프로그램을 만든다고 가정을 하죠.
다음 기능이 있는 프로그램을 만들기로하죠.
1.동물의 이름과 종류(포유류,파충류,조류,어류,양서류)를 입력 , 동물은 저장될시 자동으로 사전순서대로 저장된다.
2.저장된 동물을 종류별로 검색할 수 있다.
3.내가 어떤 동물을 저장했는지 확인할 수 있다.(ex - 고양이를 입력했는지 안했는지 확인할 수 있다.)
4.각각 위의 작업을 1,2,3번으로 시행할 수 있으며 다른 번호 입력시 프로그램을 종료한다.
이걸 만약에 한 소스파일에 적는다면 생각보다 빨리 끝날겁니다. 그러나 소스파일에 나눠적기 시작하면 생각보다 오래걸립니다.
특히 그게 C언어라면 더더욱 생각할게 많죠. 하지만 나눠주는 습관을 꼭 가져야합니다.
생각하는 사고의 과정은 각각의 차이가있습니다. 상향식으로 프로그래밍을 할 수도있고,
하향식으로 프로그래밍을 할 수도있죠. 객체지향에 가까워질수록 상향식 프로그래밍이될것이고,
구조적프로그래밍에 가까워질수록 하향식 프로그래밍이 될것입니다.
각각의 용어를 모르시는 분들을 위하여 각각의 뜻을 말씀드리겠습니다.
상향식 프로그래밍 - 작은 파트를 만들어서 그것을 아우르는 큰파트를 만들고 큰 파트들을 조합하여 프로그램을 만듦.
하향식 프로그래밍 - 먼저 큰개념을 만들고 그 큰개념에 포함될 작은 개념을 지정, 이런식으로 계속해서 작은 개념을
구현해나가는 방식으로 프로그램을 만듦.
사실 객체지향프로그래밍에 제약이 많이달린 C언어는 하향식 구현이 조금 힘듭니다. 그 이유는 포인터 때문이죠.
여튼 상향식으로 프로그래밍을 하던 하향식으로 하던 선택은 사용자의 마음이지만,
여기서는 상향식으로 구현을 했습니다. 물론 상향식이던 하향식이던 결과만 보고 추론하는건 거의 불가능에 가깝습니다.
소스파일의 분할규칙은 다음과 같습니다. 실제 있는 규칙은 아니고, 제가 프로그래밍 하면서 배운 규칙이죠.
소스코드 분할규칙
1.main은 되도록 다른 함수, 구조체(정의포함),변수를 포함하지 않는다.
2.각각의 역활별로 소스파일을 분할한다. 잘게 조갤수록 코드의 양은 늘어나지만 그 만큼 관리는 쉬워진다.
3.되도록(거의 대부분) 다른 소스파일에서의 전역변수를 직접 끌어쓰지 않는다.
(쉽게말해서 extern으로 끌어오지 않는다.)
4.각 소스파일은 가능한한 독립적이게 만든다. (계층관계를 제외)
5.한 소스파일이 다른 소스파일에 의존적이게 되는경우는 상관없지만 서로 의존되게 만들지는 않는다.
6.각 소스파일의 전역변수들은 저~~엉말 특별한 경우가 아니라면 무조건 static을 걸어준다.
여기서 중요한건 수직관계와 수평관계를 명확히 알아야 프로그래밍을 할 수 있습니다.
보통 C언어 프로그래밍에서 main소스는 모든 소스파일의 위에 있습니다.
여기서 만들 프로그램역시 마찬가지입니다. 먼저 아래 예제를 보죠.
먼저 어떠어떠한 소스파일을 만들지를 생각을 해야겠죠.
main을 담는 Main소스와 전체의 구동부를 책임질 Interface소스, 또한 동물을 저장할 Ani소스파일과
그 Ani를 배열로 가지는 AniArr소스파일을 만들것입니다.
이렇게 소스파일을 쪼개고 나서 내부을 구현하면됩니다.
일단 어느것이 수직이고 수평인지를 확실히 알아야합니다.
제일 상위개념은 당연히 Main입니다. 그리고 그 Main을 구동시키는 Interface가 바로 그 밑의 단계입니다.
여기서 Interface는 AniArr가 없으면 돌아가지 않습니다. 따라서 Interface는 AniArr의 상위개념입니다.
그리고 AniArr는 Ani가 없으면 돌아가지 않습니다. 그렇기에 AniArr는 Ani의 상위개념입니다.
제가 만든 프로그램에서는 수평적인 관계는 없고 모두 수직적인 관계 뿐입니다.
또한 전형적인 상향식 설계프로그램이라고 할 수 있죠.
이제 Ani부터 보도록 할께요.
일단 Ani의 일부분입니다.
제일 먼저 사용할 전처리를 모두 나열해줍니다. 매크로라던가, 헤더파일들을 말이죠.
그 바로밑에 사용할 구조체를 정의해줍니다.
저희는 종과 이름을 저장할 구조체를 만들고 바로 밑에는 생성자와 소멸자를 구현해줍니다.
생성자와 소멸자는 스탠다드하게 기본으로 구현해주는게 좋습니다.
쓰던 안쓰던 구현해는것이 좋습니다. 사실 이 예제에서는 배열을 다루기때문에 쓰이진 않았습니다.
그 다음 중요한건 static함수인데 이 선언은 굉장히 중요합니다.
static의 역활을 바로 위에서 가르쳐드렸듯이 이 선언이 달린 순간부터 다른 소스파일에서 사용할 수 없습니다.
즉 이 선언을 한다는것 자체는 다른 프로그래머한테 이 함수는 이 소스파일에서만 사용하는거야!!
라고 알려주는 것이죠. 여기서 swap과 InsertionSort는 이 소스파일 내에서만 사용할 수 있습니다.
이제 나머지 함수들을 적어줍니다. 각각의 구동부에대헤서 구체적인 구현을 하는것으로 ani소스파일의 구현은 끝이납니다.
이 함수들은 다른 소스파일에서 사용할 함수들입니다.
따라서 static을 붙혀주지않습니다. 만약 생각해보니 다른함수에서 쓰면안될것 같다면 static을 붙혀주시면됩니다.
이번에는 AniArr소스파일을 보겠습니다. 이번 역시 마찬가지입니다.
생성자와 비슷한 역활을 하는 InitAniArr와 DeleteAniArr는 선두에 적어줍니다.
그리고 여기서는 Ani에서 사용할 구조체와 함수의 선언만을 가져옵니다.
구조체의 정의를 가져올순 없습니다. 만약 그렇게 되면 다른 소스파일에 정의가 또있으므로 중복정의가되어 링킹시에 에러가납니다.
그 다음 역시 외부에서 사용할 함수를 구현해줍니다.
Interface역시 구현해줍니다. 이 Interface는 실질적으로 가진 전역함수는 없으며 사실 실질적인 계산기능역시 없는
보여주기위한 소스파일입니다.
마지막으로 main입니다. 정말... 심플하기 그지없습니다.
기억해 두실것은 main은 최대한 심플하게 만들고 쪼갤수 있는 단위로 쪼개야된다는 겁니다.
4.헤더파일의 사용법
사용하시다보면 헤더파일과 소스파일의 사용법에 큰 차이가 없다고 느끼시는 분들이 있을겁니다.
저도 그래왔구요. 그러나 헤더파일과 소스파일의 역활차이는 사실 조금 큽니다.
둘의 역활에 왜 차이가 있느냐하면 그건 타이밍의 차이입니다. 프로그래밍의 순서는 다음과 같습니다.
프로그램의 타임은 4개로 나눌 수 있고 그중에 실행시간인 Run Time을 제외하면 프로그래밍을 하는 타이밍은 총 3가지입니다.
물론 프로그래밍시에 Run Time이 중요하다는 말은 아니고 헤더파일의 역활을 알기위해서는 이 3가지의 타임을 확실히 알아야합니다.
헤더파일과 소스파일의 결정적인 차이점은 합쳐지는 타이밍에 있습니다.
헤더파일이 소스파일에 합쳐지는 순간은 전처리타임이고 전처리타임에 전처리기가 모든헤더파일을 소스파일부분에 추가시켜
새로운 소스파일로 만들어줍니다. 정확히 말하면 새로운 소스파일 자체를 만드는것은 아니고 개념적으로 합쳐준다는 것이죠.
그전에는 헤더파일과 소스파일은 따로놀았다면 지금부터는 헤더파일과 소스파일은 한몸체가 되는것이죠.
이렇게 한몸체가된 소스파일은 다시 컴파일러에 의해서 목적파일로 바뀌면서 물리적으로도 한몸이 됩니다.
이렇게 완성된 목적파일은 소스파일당 하나가 존재하는데, 예를들어서 우리가 소스파일을 4개를 만들었다면
목적파일 역시 4개가 생성됩니다. 또한 이 목적파일들을 링커가 모조리 묶어서 하나의 프로그램으로 만들어 주는것이죠.
일단 헤더파일과 소스파일의 가장큰 차이점은 헤더파일은 한 소스파일에서만 사용한다는 보장이 없다는 것이죠.
일단 헤더파일은 이론적으로 사용될 수도있고 안 사용될 수도 있는데 더 많은데서 사용될 가능성도 충분히 있다는 것이죠.
예를들어서 제가 위에 작성했던 코드들만 해도 stdio.h , stdlib.h, string.h, stdbool.h는 기본으로 깔고 시작하죠?
main을 제외한 모든 소스파일에 이 헤더파일들이 들어있습니다.
즉 헤더파일은 기본적으로 다른 소스파일에 중복으로 사용된다는것을 상정하고 프로그래밍을 한다는겁니다.
이러한 성질로 인하여 잘못사용시 다음과 같은 문제가 생깁니다.
이코드는 사칙연산을 구현하는 Arthmetic파일과 그것을 실행하는 구동부인 Main, 그리고 양쪽모두에 사용되는 Print헤더가 존재합니다.
사실 별 문제없어보이며 빨간 밑줄이 없는거로 봐서는 일단 전처리타임과 컴파일타임에는 문법상 아무 문제가 없는것 같습니다.
하지만 사실 이 코드를 실행하는순간 바로 에러가 날겁니다.
링크에러가 나는데 Print를 중복선언을 했다고 합니다.
엥? 난 중복선언한적 없는데? 라고 하실수 있을겁니다. 그러나 전처리 규칙에 의하여 Print를 중복선언한게 맞습니다.
Main과 Arithmetic모두 Print헤더를 사용합니다. 그 말은 Print함수는 Main에도 존재하고 Arithmetic에도 존재합니다.
즉 중복으로 선언된다는 것이죠. 따라서 이는 링커에서 에러를 내게됩니다.
그러면 이 문제를 어떻게 해결해야하느냐? 이 말대로라면 헤더파일은 완전 쓸모없는거죠.
이런식으로 사용하는 헤더파일이 한두가지가아닌데 이렇게 사용할수 없다면 안사용하는게 낫죠.
좀 생각해보면 이 문제를 해결할 방법이 없을것 같습니다. 그러나 사실 해결할 수 있는 좋은 방법이 있습니다.
만약 이 문제를 해결할 수 없었다면 저희는 stdio.h나 stdlib.h같은 헤더파일을 사용하지 못하고 있겟죠.
만약 어떠한 헤더파일을 사용할때 그 헤더파일에 사용할 변수와 함수의 본체를 구현하지말고
정의만 해놓고 구현은 동일한이름으로 소스파일을 만들어서 거기다가 구현해주면됩니다.
이 방법 하나로 위의 문제점을 해결할 수 있습니다.
C언어 규칙에서 구현은 단 한번밖에(오버로딩과 오버라이딩을 허용하지 않는다.) 할 수 없지만
선언은 여러번할 수 있기때문입니다.
위의 Main과 Arithmetic모드 print헤더파일을 사용하지만 전처리를 거친다고 해도 본체가 없기때문에 단순선언이라보고 문제를
일으키지 않습니다. 사실 모든 헤더파일은 이런식으로 정의되어 있습니다.
stdio.h같은 헤더파일을 뜯어봐도 본체는 구현되어있지 않습니다.
본체는 다른 폴더에 존재하죠. 쌍으로 존재하는 것입니다.
이 예제를 통해서 제가 말씀드리고 싶었던 것은 헤더파일은 소스파일과는 달리 선언만이 존재해야한다는 것입니다.
이는 중복성을 없애기위해서입니다. 만약 헤더파일에 정의를 한다면 위와같은 문제를 항상 일으킬 가능성을 내포하는 것이죠.
헤더파일을 만들시에도 규칙이 몇개 존재합니다 그 규칙은 아래와 같습니다.
헤더파일 생성규칙
1.소스파일을 만든다면 되도록이면 그 이름과 같은 헤더파일을 만들어주는것이 좋습니다.
2.그 헤더파일에는 전처리,선언 등.. 정의와 전처리 매크로를 제외한 모든것을 넣는다.
3.반드시 정의는 넣지 않는다. 단 구조체 정의는 넣을 수 있다.
4.헤더파일을 보면 대략작으로 쌍으로 연결될 소스파일이 무슨역활을 하게될지 알게해야한다.
여기서 한가지 살펴봐야할 것은 3번입니다.
변수와 함수는 단순히 선언만 넣어줘야하는데 선언은 예를 들면 아래 같은 것들이죠.
여기서 1번과 2번과 3번은 선언이고 4번은 구조체의 정의입니다.
변수를 선언할때는 무조건 extern을 선언해줘야합니다.
만약 extern을 사용하지 않는다면 그것은 선언이 아니라 정의가 됩니다.
(초기화 여부와는 상관없습니다. 그리고 전역변수는 0으로 자동으로 초기화가 됩니다.)
문제는 구조체의 선언, 혹은 정의인데 3번은 선언이고 4번은 정의입니다.
여기서 구조체의 정의는 다른 소스파일과 조금도, 1%도 상관이 없습니다.
같은 소스파일에서 구조체의 중복정의는 불가능하지만
다른 소스파일에서 구조체의 중복정의는 가능합니다.
그 이유는 컴파일러는 다른 소스파일에서의 같은이름의 구조체 정의는 알지 못하기 때문입니다.
그 이유는 설명하자면 조금 길어지니까 다음에 설명하도록 하겠습니다.
근데 3번과 4번이 왜 중요하느냐? 그 이유는 3번과 4번중에 하나만 사용하는게 아니라,
3번을 헤더파일에 사용하는 경우도 있고 4번을 헤더파일에 사용하는 경우도있으며
둘을 사용할때의 효과역시 다르기 때문입니다.
그 예시를 아래에서 보도록합시다. 일단 먼저 4번방식으로 프로그래밍을 했을때의 예시입니다.
헤더파일에 아예 struck Book을 정의를 했다면 이런식으로 프로그래밍하는것이 가능합니다.
중요한건 main입니다. 여기서 Book a;라는것도 눈여겨 볼만한데 Book을 아예 인스턴스로 정의할 수 있습니다.
게다가 Book의 멤버에 접근역시 가능합니다. 이 방식은 C++에서 class를 구현할때 사용하는 방식입니다.
이 방식의 가장 중요한점은 직관적으로 인스턴스를 만들 수 있다는점, 그리고 그 인스턴스의 변수에 직접접근이 가능한것이죠.
이 방법말고 다른 방법은 다음 챕터로 넘어가서 보실께요.
5.opaque(불투명) 포인터
이번에는 3번방식으로 코딩해보았습니다. 밑의 에러구분까지 같이 보여드렸는데요 중요한건 이겁니다.
Book소스파일에는 명시적으로 정의되어있습니다. 그러나 Main에서는 Book이 정의가 되어있지 않습니다.
본체가 없이 선언만 되어 있는 상태입니다. 문제는 선언만 되어있기 때문에 당연히 개체를 생성할 수 없고
또한 개체가 생성되지 않으므로 내부변수에 접근할 수도(Main입장에서는 아예 내부변수가 없죠.) 없습니다.
그럼 이 방식은 아예쓰지 않는것인가? 그런건 아닙니다. 사실 제가 짠코드에서 윗부분에 보면 이렇게 짠 부분이 있습니다.
3번 목차 소스코드의 분할에서 보시면 사용한 예가있죠.
Book멤버에 직접접근 할 수 없기에 다른 함수를 통해서 우회적으로만 사용이 가능합니다.
또한 Book이 명시적으로 존재하지 않는 개체이기 때문에 반드시 포인터를 사용해줘야합니다.
위를 에러없이 돌리려면 아래같은 코드가 필요합니다.
보시면 정상작동을합니다.
main을 보시면 Book을 포인터형으로 선언하였고 포인터형은 형식의 완전성을 가리지 않기에 선언이 가능합니다.
포인터 변수 b에는 저희가 만든 newBook을 만들었습니다. 이러하게 인스턴스를 반환하는 함수를 생성자라고 합니다.
빨간원이 두개있는데 위의 newBook은 생성자인 셈이지요. 아래의 BookGetName과 BookGetSerial 각 인스턴스에 직접접근할수 없는
main입장에서 멤버를 볼수있는 방법입니다. 이러한 함수들을 접근자라고 부릅니다.
이 방식은 main에서 Book멤버를 직접적으로 볼수있거나 수정하거나, 심지어 무슨멤버가 있는지 조차도 확인할 수 없습니다.
이 방식은 굉장히 불편해 보이지만 한편으로는 불편한 만큼 굉장한 안정성을 보장한다고 할 수 있죠.
이렇게 멤버를 직접적으로 접근하지 못하게 하고 우회해서 접근하게 만드는 것을 은닉화라고 부릅니다.
그리고 이 은닉화 방식은 윈도우즈API뜯어보면 굉장히 많이 사용된다는 것을 알 수 있습니다.
C++에서는 위의 방식을, C에서는 아래의 방식을 많이씁니다.
왜 C++에서는 위의 방식을 사용하냐면 C++에서는 private가 있고 C에서는 private가 없기때문이죠.
이렇게 사용하는 방식을 불투명(Opaque) 포인터라고 부르며 C로 구현하는 객체지향의 핵심이라고 할 수 있습니다.
왜 이름이 불투명이냐하면 투명하고 구조체의 내부에 접근할수있는 4번방식과는 달리 이 방식은 불투명하게
내부를 볼 수 없기 때문이죠. 대신 모든 자료를 함수를 통해서만 확인할 수 있습니다.
그리고 이 방식이 불투명 구조체가 아닌 불투명 포인터인 이유도 여기서 나옵니다.
구조체의 몸체가 없기에 모든 자료형을 포인터로 밖에 만들 수 없습니다.
구조체의 인스턴스를 직접만드려면 그 내부의 정보를 알아야하는데 여기서는 은닉화되어서 저희가 알 방법이 없으므로
우회적인 방법밖에 못 사용하는 것이죠. 이 방식은 굉장히 중요한데 생각보다 다루는 곳이 별루 없더라구요.
꼭 아시기 바랍니다.
6.객체지향 기법
이상으로 C에서 사용하는 모듈화와 그에 관련된 기법들을 사용하였는데 맛뵈기로 객체지향을 가르쳐드릴께요.
이 파트는 객체지향을 모르시면 그냥 넘어가시면 되구요, 객체지향을 아시는 분들도 이 방법이 무조건
C에서 객체지향을 만드는방법이다!! 까지는 아니고 한가지의 방법론이라는 것을 알아두셨으면 좋겠어요.
일단 객체지향 기법에서 가장중요한것은 보통 3가지쯤으로 요약할 수 있습니다.
1.멤버함수와 멤버변수가 존재하는 클래스 모델을 구현할 수 있느냐?
2.public, protected, private를 사용하는 캡슐화를 구현할 수 있느냐?
3.상속을 구현할 수 있느냐?
더 많이 요구되겠지만 일단 이 세가지가 없다면 객체지향이라고 하기조금 애매해지겠죠.
결론부터 말하면 셋다 구현이 가능합니다. 단 여러분이 아시는 객체지향모델과는 조금많이 달라지게 되고
사용자가 조건을 잘지정해야한다는 차이점이 있지요. 그러면 저 세가지를 하나씩 보여드리겠습니다.
1.속성과 메소드의 구현
가장 구현하기 쉬운것입니다. 먼저 안에 사용할 변수와 함수포인터를 지정해주시고, 그 함수포인터에 연결할
함수를 지정해주시면됩니다.
그리고 new로시작하는 구조체의 인스턴스를 반환하는 함수를 만듭니다. 이 함수를 생성자라고 부릅니다.
그리고 중요한건 함수포인터와 연결될 함수들을 구현할때 반드시 static으로 지정하여주시고,
자기자신을 파라메터로 넘기고 이름은 반드시 this로 지정해줍니다.
이 this라는 포인터는 항상 멤버함수를 사용하는 구조체 자기자신을 파라메터로 넘겨주게됩니다.
이런 포인터를 this포인터라고 부릅니다. C++에서도 원리는 똑같습니다. 여기서는 저희가 수동으로 넘겨줍니다.
그리고 생성자에서는 항상 만들어지는 즉시 내부의 변수를 모두 초기화시켜주고
함수포인터를 함수들과 연결시켜줍니다. 이제 실행결과를 보시죠.
어디서 많이 본거같은데? 이거 C++아닌가 착각이 드실리는 없지만겁니다. 여기서 중요한건 b->GetName(b)를 보시면 아시겠지만
자기자신을 반드시 넘겨주셔야합니다. 여기서 넘어간 자기자신은 this포인터로서 역활을 하게 됩니다.
2.캡슐화 구현
이 부분 방법이 좀 많긴한데 저는 헤더파일 분할로 이를 구현하도록 하겠습니다. 위 예제와 연장선이니까 잘봐주세요.
먼저 헤더파일을 하나로 만드는것이 아니라 3개 혹은 4개로 필요에 따라 더 분활하셔야합니다. 구도는 아래와 같습니다.
Protected를 구현하지 않는다면 안써도 됩니다.
이렇게 세개의 부로 분할해서 헤더파일을 만들고 그 헤더파일을 총 정리하는 헤더파일에 담습니다.
그리고 마지막의 객체모델은 가장 높은 수준의 보안에 만들어줍니다.
가장높은수준이 Private면 Private에 구현을 protected면 protected에 구현을 해주면됩니다.
위의 Book 구조체를 예시로 구현하도록하겠습니다.
이렇게 각구도별로 밖에 노출해줄 데이터를 제한해서 헤더파일을 만들어줄겁니다.
Private부에보시면 아시겠지만 name과 serial은 private에 존재합니다. 이는 이를 구현하는 자기자신의 소스파일에만 보여주고
나머지에는 public부만 존재하는 BookPublic을 헤더로 달아줄겁니다.
아래는 이를 사용한 예제입니다.
위에 헤더를 보시면 Book에는 Private를, Main에는 Public을 사용해서 노출도를 제한했습니다.
그럼에도 불구하고 프로그램은 제대로 돌아갑니다.
그리고 각각의 소스파일에서 내부접근도에서 차이를 보입니다. public만 공개된 Main소스에서는 name과 serial에 접근 불가하지만
private까지 공개된 자기자신의 소스파일에서는 모든 멤버에 접근할 수 있습니다.
3.상속 구현
위의 방식에 비해서 가장 어거지로 돌아가는 방식인데요.. 근데 실제 C++돌아가는 방식도 이방식인건 맞습니다..
더깔끔하게 돌아긴하는데요, 이는 사용자들끼리의 약속이 조금더 필요하긴합니다.
왜냐하면 C에서는 자타입확인 방법이 없기 때문이죠.
먼저 Book에서 상속을 받는 Magazine을 만든다고 치면 BookProtected(없으면 public)을 헤더에 포함시킵니다.
이로서 상속의 준비는 완료입니다.
그 다음 Book*으로 포인터를 하나 추가해줍니다. 이 포인터가 바로 상위 객체를 상속받는 포인터입니다.
일반적인 객체지향에서 이런방식으로 구현하진 않는데, 사실 내부적으로는 전부 이렇게 작동합니다.
C에서는 명시적으로 적어주는 방법밖에 없습니다.
그리고 생성자를 구현할때 반드시 상위 객체를 구현해주고 연결해주는 작업을 해줍니다.
즉 객체를 만들때 그게 부모개체가 존재한다면 부모개체의 생성자까지 호출이 되는 것이죠.
이런식으로 상속을 구현해 줄 수 있습니다.
Magazine객체는 Book객체를 상속받았지만 Book의 소스코드 알파벳 한자도 건드리지 않았습니다.
이런 보안이 객체지행 프로그래밍의 장점이라고 할 수 있죠.
이제 main에 똑같이 적용시키면됩니다. 이로써 상속모델역시 구현가능하다는 것을 알 수 있습니다.
7.마치며
상당히 오랜시간 작업을 했는데 힘드네요. 근데 예제를 만들때 이제 프로그램이 점점커져서 어느수준까지 만들어야할지
도무지 감이 안잡혀서 지우고 만들고 지우고 만들고를 반복하느라 시간이 오래 걸렸습니다.
다음 시간에는 라이브러리에 대해서 배우도록하겠습니다.
'프로그램 개발(분석, 설계, 코딩, 배포) > 2. 개발' 카테고리의 다른 글
RSA를 이해하기 위한 코드 (0) | 2023.08.17 |
---|---|
코드_IP 세팅 (0) | 2023.03.21 |
[작업중]개발_오라클_오라클 11g 설치 및 삭제 (0) | 2022.11.21 |
개발_데이터베이스_오라클 버전 (0) | 2022.11.21 |
개발_코드 검증 사이트 (0) | 2022.11.21 |