C++는 1983년 AT&T Bell 연구소의 Bjarne Stroustrup이 C언어를 바탕으로 OOP(Object Oriented Programming)개녑을 도입한 언어입니다.



#include<iostream>

int main() {
    std::cout << "Hello, world!" << endl;
}    

  그럼 먼저 OOP에 대해 알아봐야겠죠.

  프로그램을 객체단위로 나누고 이 객체들의 상호작용을 서술하는 방식을 말합니다.


  초기의 프로그래밍 방식은 절차적 프로그래밍이었습니다. 입력을 받아 명시된 순서대로 처리한 다음 결과를 내는 명령어 모음으로 인식하고 있었습니다. 

  C언어에서도 이 방식의 잔재를 확인할 수 있는데요. 바로 Goto문입니다. 코드를 주르륵 읽어 내리다가 Goto문을 만나면 북마크로 이동하는 이러한 방식이 기존의 절차적프로그래밍에 익숙한 프로그래머들을 끌어들이기 위해 남겨놓은 문법이었습니다.

  하지만 이런 방식은 조금만 복잡해져도 순서도로 나타내는 것이 불가능할 정도의 어려움을 겪게됩니다.


  이를 해결하기 위한 방식이 구조적 프로그래밍 방식이었습니다. 프로그램을 함수단위로 나누고 프로시져끼리 호출을 하는 구조적 프로그래밍 방식은 큰 문제를 해결하기 위해 작은 문제들로 나누어 해결하는 Top-down 방식을 지향합니다.

  그러나 이 방식은 데이터 처리방법을 구조화했지만 데이터 자체는 구조화 하는데 실패했습니다. 이상한 데이터가 이상한 함수에 전달되어 데이터를 오염시키는 문제가 발생하는 것을 지역변수나 struct 등으로 제어해야 했지만 더 근본적인 해결책이 필요했습니다.


  또! 이를 해결하기 위한 방식이 객체지향 프로그래밍입니다. Top-down 방식과는 반대로 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 이 객체들을 조합해서 큰 문제를 해결하는 Bottom-up 방식을 도입하여 수정없이 객체를 재사용할 수 있도록 했습니다. 특히 GUI가 등장한 후, 객체지향 프로그래밍은 위와 같은 문제의 해결책으로 급부상하게 되었습니다.

  객체지향 프로그래밍의 큰 특징들은 다음과 같습니다.


1. 캡슐화(Encapsulation)

  계층적으로 분류한 기능과 특성의 모음을 클래스(Class)라는 캡슐에 집어 넣어 코드 수정 없는 재활용을 지향하는 것입니다. 캡슐화는 클래스, 타입, 인스턴스, 생성자, 소멸자와 같은 객체지향 프로그램 방식의 기초를 형성합니다.


  캡슐화는 두가지 측면으로 생각하실 수 있습니다.

1) Class 내부에 관련된 변수와 메소드를 묶어 관리하는 것

2) 정보은닉: 프로그램의 동작과정을 외부로 노출시키지 않도록 하는 것


캡슐화 구현을 위해 접근지정자라는 것을 지정합니다. 이는 클래스 내부의 해당 구역이 어디까지 접근가능한지를 알려주는 역할을 합니다.


private: 클래스 내부에서만 접근 가능

protected: 클래스 내부 + 자식 클래스 멤버함수

public: 클래스 내부 + 자식 클래스 멤버함수 + 외부함수



ex)


#include<iostream>
#include<cmath>
using namespace std;

class Pythagoras {
private:
    int x;
    int y;

    void PythaZ = sqrt(x * x + y * y);
    

}

위의 간단한 예제 처럼 x와 y 값을 입력하면 빗변의 길이를 구하는 공식까지를 묶어 한번에 처리할 수 있도록 하는 것을 말합니다.

2. 상속(Inheritance)

  이전의 라이브러리 보다 논리적이고 기능과 데이터를 계층으로 분류해 사용편의성을 도모하는 기능을 말합니다. 자식 클래스가 부모 클래스의 기능을 받아 쓰는 것이라고 이해하시면 편합니다.

  

  마이크로소프트의 MSDN에 있는 단일상속 예제를 통해 상속이 어떻게 쓰이는지 한번 알아보겠습니다.

(링크: https://msdn.microsoft.com/ko-kr/library/84eaw35x.aspx)


#include<iostream>
using namespace std;
class Document {
public:
    char *Name;
    void PrintNameOf();
};

void Document::PrintNameOf() {
    cout << Name << endl;
}

class Book : public Document {
public:
    Book( char *name, long pagecount );
private:
    long PageCount;
};

Book::Book( char *name, long pagecount ) {
    Name = new char[strlen(name) + 1];
    strcpy_s(Name, strlen(Name), name);
    Pagecount = pagecount;
};


  Document 클래스의 PrintNameOf 함수를 구현할 때 부모 클래스로 부터 상속받아 참조하게 됩니다. Book 클래스에서 가져온 Book의 생성자(Book::Book)은 Name에 접근할 수 있게 됩니다.


위의 코드는 이렇게 해석할 수 있습니다.

Document 클래스 - Book 클래스

      |                  

PrintNameOf 구현     Book 생성자


  이렇게 상속을 하면 윗쪽 클래스를 재사용할 수 있습니다. 만약 리그오브레전드 같은 게임에서 공통적인 능력치들인 체력과 마나, 레벨 등의 능력치를 상속을 통해 활용하면 효과적으로 구현할 수 있습니다. (하지만, 캐릭터마다 능력치를 구현하고 정의했다면?? : 버그 패치하는데 한달씩 걸릴지도 모를 일입니다.)


  상속받았을 때 킨드레드처럼 기존의 능력과는 다른 기능을 추가할 때에 재정의할 필요가 있습니다. 이때 재정의(Override)를 사용합니다.

  이와 달리 오버로드(꾸에에엑 ㅈㅅ.. Overload) 는 이름만 같고 인수나 타입이 서로 다른 클래스를 만드는 행위입니다. 가렌이나 리신처럼 마나라는 클래스에 마나라는 이름만 같지만 저절로 차는 것이 아닌 때려야 분노가 차는 그런 클래스를 만들 때 사용합니다.


  하지만 상속을 잘못 사용하다가는 프로그램 보안에 큰 구멍이 생길 수 있습니다. 자식클래스가 부모클래스 정보를 가져오기 때문에 잘못하면 내부구조를 드러내 해커들의 밥이 될 수 있기 때문에 private 사용을 강권합니다.

   



3. 다형성(Polymorphism)


  다형성



#include<iostream>
using namespace std;

void Overload(int i) {
	int k;
	for( k = 0; k < i;k++){
    cout << "꾸에에엑~" << k << endl;
	}
}

void Overload(int i, int j) {
	int l,m;
	for(l = 0;l < i;l++){
		for(m = 0;m < j;m++){
			cout<< "꾸에에엑!"<< l << m << endl;
		}
	}
    
}

int main() {
    Overload(4);
    Overload(6,7);

    return 0;
}


  위의 예제 처럼 다형성은 같은 함수이름을 사용하지만 변수와 함수 속 내용을 다르게 해서 전혀다른 결과를 만들어 낼 수 있습니다.

  사실 위의 예제는 오버로드라는 (생명체가 아니고..) 특성에 대해 알려드리기 위해 보여드린 예제이며, 다형성을 사용하기 위해 상속관계를 잘 활용해서 접근해야 합니다.

  다형성을 사용하면 본격적인 객체지향 프로그래밍을 할 수 있습니다. 타이핑을 덜할 수 있으며, 코드를 완전히 컴파일 할 수 있어 소스코드를 배포할 필요가 없어집니다.


펑션포인터 (Function Pointer)

  Function Pointer는 함수의 주소를 기억하고 해당 함수를 실행하는데 사용합니다.


#include<iostream>
#include<cmath>

using namespace std;

int Pythagoras(int x, int y) {
    return sqrt(x*x + y*y);
}

int Sum(int x, int y){
    return x + y;
}

int main() {
    int (*fp)(int, int) = NULL;
    int num = 0;

    fp = Pythagoras;
    num = (*fp)(3, 4);
    cout << num << endl;

    fp = Sum;
    num = (*fp)(3, 4);
    cout << num << endl;
}    

앞서 만든 피타고라스의 빗변을 구하는 공식에 펑션 포인터를 구성해 보았습니다.

Pythagoras 함수의 주소를 fp로 구현을 했습니다.

Pythagoras 함수는 int 두개를 받아 int 하나를 돌려주므로 출력 (포인터)(입력, 입력) 으로 선언할 수 있습니다.


Pythagoras 함수를 fp 에 저장한 뒤, 이 주소값을 활용해 num으로 리턴값을 돌려받습니다.

그리고, 그 값을 출력하는 것으로 프로그램을 마무리합니다.


이 점을 활용하면 더욱 유연한 코딩을 가능케합니다.

  성적표를 분석하는 프로그램에서 전체 학생들의 성적 중 특정과목의 정보를 얻고 싶다면 그 함수들의 주소값만 바꿔주면 앞서 만든 함수의 재사용이 가능해집니다.

  마치 USB에 선풍기를 꽂았다가 메모리스틱을 꽂았다가 외장하드를 꽂는 것 처럼 포트 하나로 다양한 일을 하게 해줍니다.

  노트북에 전용 포트를 주르륵 달 필요 없이 USB로 이 일을 가능케 해 보다 이쁜 노트북을 만들 수 있는 것과 같습니다.

+ Recent posts