예외 핸들링은 Memory Fault와 같은 Critical한 Exception을 핸들링하기 위함이며 또한 디버깅에 도움을 주기 위함이다. Windows에서 C/C++로 개발을 할 때 예외를 핸들링할 수 있는 방법은 크게 세가지가 있다.

 

1. __try ~ __except


Windows OS에서 제공하는 예외 처리 방법이며, 일반적으로 SEH(Structured Error Handling)이라 부른다. Access Viloation과 같이 프로그램 상의 오류로 인해 발생하는 대부분의 예외를 처리 가능하다. 관련 키워드는 __try, __except, __finally, __leave가 있다.

 

__except의 부분의 상수는 크게 세 가지가 존재한다

  • EXCEPTION_EXECUTE_HANDLER
    • 예외 처리 루틴을 실행하라는 의미며, 여기서 의미하는 예외 처리 루틴은 __except 블록 내의 코드를 의미한다.

 

  • EXCEPTION_CONTINUE_SEARCH
    • try ~ catch에서 예외를 던지는 것과 같은 효과이다. 즉, 바깥 함수에 다시 __try ~ 구문이 기다리고 있지 않다면 프로그램은 예외를 내게 된다.

 

  • EXCEPTION_CONTINUE_EXECUTION
    • 예외가 발생한 라인부터 다시 실행을 시도한다. 만약 예외가 해결되지 않은 상황에서 다시 실행을 할 경우 무한루프가 발생한다.

 

1.1 장점

- 구조화된 예외처리를 수행하기 때문에 코드가 깔끔해진다.

 

 

1.1 단점

- __try ~ __except 구문의 가장 큰 단점은 소멸자가 존재하는 클래스와 함께 사용할 수 없다는 점이다.

- 소멸자가 정의된 클래스의 인스턴스를 __try 블록 안에서 생성하게 되면 컴파일 에러가 발생한다.

- try ~ catch 구문과 함께 사용할 수 없다.

 

2. try ~ catch


표준 C++에서 제공하는 예외 처리 방법이며, 일반적으로 C++ Error Handling이라 부른다. try ~ catch 구문은 throw 키워드를 이용해 코드상에서 명시적으로 발생시킨 예외를 핸들링한다. try ~ catch 구문은 내부적으로는 __try ~ __except의 예외처리 구조(SEH)를 기반으로 구현되어 있다. catch에서 일치하는 데이터 타입이 없어 예외가 핸들링 되지 않으면, 프로그램은 최종적으로 예외를 발생시키고 비정상 종료된다. (관련 키워드 : try, catch, throw)

 

3. Unhandled Exception Filter


앞서 1,2번인 SEH나 C++ 예외 핸들링에 의해 핸들링되지 않은 예외를 최종적으로 핸들링할 수 있는 Callback 함수를 등록하는 방법이다.

 

 

4. Reference


[1] https://kuaaan.tistory.com/435

[2] https://kuaaan.tistory.com/103

[3] https://elky84.github.io/2011/08/24/exception_handling_in_cpp/

 

단순 요약


1. API는 C 프로그래밍에서 사용 된다.

2. MFC는 C++ 프로그래밍에서 사용 된다.

즉,  MFC는 API와 같은 것이나 객체 지향 개념이 도입된 C++에서 API를 기능별로 클래스화 한 형태로 만든 것이다.

 

MFC


Microsoft Foundation Class의 약자로 마이크로소프트에서 만든 클래스이다.

 

C++ 언어를 사용하는 경우 API 함수들이 모두 C 형태로 제공되기 때문에 C++의 특징인 객체지향의 형식에도 맞도록 재구성의 필요성이 증대했다. 이에 Microsoft에서 C++ 개발자를 위해 API 함수와 API 함수가 사용하는 구조체들을 모아서 클래스를 만들어 놓았는데 이 클래스의 집합을 MFC라 한다. 

 

MFC의 시초는 AFX(Application Framework)에서 유래되었다 할 수 있다. AFX는 1990년 마이크로소프트가 윈도우 API 함수를 캡슐화하여 만든 라이브러리이다. AFX는 MFC의 전 단계로, 수정을 거쳐서 지금의 MFC가 완성된 것이다. 1993년 이후부터 MFC 라이브러리는 Visual C++에 포함되어 제공되고 있다.

 

 

MFC의 특징


  • 객체 지향 프로그래밍
    • C++의 모든 문법이나 특성을 그대로 이어 받는다.
    • 윈도우의 Object를 포함하는 시각적 객체를 사용한다.

 

  • 이벤트 처리방식
    • 사용자 또는 시스템이 윈도우에 어떤 동작을 했을 경우 그에 대응하여 움직이는 사건의 중심을 프로그램이라 하며 이것을 Event Driven Programming이라 한다.

 

  • 메세지 처리방식
    • 윈도우 이벤트가 발생했을 경우 해당 이벤트를 메세지를 통해 알려주고 그 메시지를 받아서 그에 따른 Output을 한다. 이를 Message Driven Programming이라 한다.

 

MFC의 장점


  • 프로그램 개발 시간을 크게 단축시킨다. 기존의 Win32 API를 이용할 때, 개발자가 직접하던 많은 실행 함수에 대한 번거로움을 개선한다.
  • 객체지향을 이용하여 C++ 프로그래밍을 하면서도 Win32 API 함수를 그대로 사용할 수 있다.
  • 동적 메모리 할당 영역 검증 및 관리, 디버깅 등의 기능을 활용해 버그가 적은 안전한 프로그램을 작성할 수 있다.
  • 다른 운영체제로 이식성이 좋다. MFC로 작성된 프로그램을 매킨토시나 유닉스 버전으로 포팅할 수 있다.
  • 개발자로 하여금 손쉬운 GUI 기반 프로그램 제작 환경을 만들어 준다.

 

MFC의 계층도


기본적으로 MFC 프로젝트를 생성하면 위와 같은 구조도로 이루어져 있으며, 위와 같은 계층 구조에서 크게 벗어나지 않는 범위 내에서 파생 클래스로 모든 클래스를 상속 받아서 만들어지게 된다.

따라서 A부터 Z까지 모든 것을 프로그래머가 프로그래밍을 하는 것이 아니라, 공통적인 부분은 MFC 클래스에서 제공하는 기능을 그대로 이용하고, 고유한 기능만 프로그래머가 커스터마이징하여 넣어주면 되기에 프로그래머 입장에서 손쉽게 코딩할 수 있다.

 

Reference


[1] https://yyman.tistory.com/490

[2] https://coding-factory.tistory.com/141

'Interest > 기타' 카테고리의 다른 글

[Windows Programming] main, wmain, _tmain  (0) 2020.03.18
[C/C++] 예외 핸들링(Exception Handling)  (0) 2020.03.18
[Windows Programming] API와 SDK  (0) 2020.03.18
[Windows Programming] HWND, HINSTANCE, HDC  (0) 2020.03.18
VI 기능 단축키  (0) 2020.03.18

응용 프로그램과 API


컴퓨터에서 실행되는 모든 프로그램을 운영체제가 제공할 수 없다. 다양한 사용자의 요구를 운영체제가 모두 반영할 수 없기 때문이다. 따라서 운영체제에서 제공하는 프로그램과는 별도로 개발자들이 해당 운영체제에서 동작하는 프로그램을 개발할 수 있는데 이렇게 개발된 프로그램을 응용 프로그램(Application Program)이라 한다.

 

응용 프로그램 개발자들은 일반 사용자와 마찬가지로 운영체제를 통해서 하드웨어를 간접 제어하고 운영체제가 관리하는 정보에 접근할 수 있다. 차이가 있다면 일반 사용자는 운영체제에서 제공하는 프로그램들(제어판, 탐색기 등)을 사용해서 작업을 진행하지만 응용 프로그램 개발자들은 조금 더 효과적인 처리를 위해서 운영체제가 제공하는 함수를 통해서 이런 작업을 한다는 것이다.

 

여기서 응용 프로그램 개발자들이 해당 운영체제에서 동작하는 프로그램을 쉽게 만들 수 있도록 운영체제가 제공하는 함수의 집합체를 API(Application Programming Interface)라고 한다.

 

 

SDK


API를 사용하기 위해서는 프로그래밍 언어와 함께 해당 프로그램 언어를 컴파일 할 수 있는 컴파일러도 제공되어야 한다. 메모장과 같은 편집기에서 작업하면 불편하기 때문에 조금 더 편리한 소스 편집기도 필요하다.

 

이처럼 응용 프로그램을 개발하기 위해 필요한 프로그램 집합체, 즉 개발 환경을 SDK(Software Development Kit, 소프트웨어 개발 키트)라고 한다. 우리가 개발하는 Visual Studio도 개발에 필요한 통합 환경을 제공하기 때문에 SDK라고 할 수 있다. 하지만 개발에 필요한 기능만 가지고 있는 것이 아니라 개발 계획을 세우는 기능도 포함되어 있고 개발 후에 프로그램을 유지보수 하는 개념과 사용자에게 편리하게 배포(설치 파일을 만들고 사용자에게 전달하는 기능 등)하는 기능까지 포함하고 있기 때문에 SDK 보다는 조금 더 넓은 의미의 통합 개발 환경을 제공한다.

 

Reference


[1] https://blog.naver.com/tipsware/220918842965

- HINSTANCE  와 HWND 차이

HWND와 HINSTANCE 차이


 

HWND =  Handle Windows의 약자이며 윈도우의 핸들 번호를 저장해서 사용한다. 하나의 프로그램에서 많은 양의 윈도우를 띄울수 있다. 즉 윈도우창의 번호들(HWND) 로 구분하는것이다.

 

HINSTANCE = 핸들 인스턴스  , 프로그램의 인스턴스 식별자 , 쉽게 보면 프로그램 자체의 실체화된 주소.

 

결론 = HINSTANCE는 프로그램 자체의 핸들이며 , HWND는 프로그램안의 윈도우창의 번호

 

Handle Device Context


Device Context란 출력에 필요한 정보를 가지는 데이터 구좇, 좌표, 색 굵기 등 출력에 필요한 모든 정보를 담고 있다.

결국 HDC란 DC의 정보를 저장하는 데이터 구조체의 위치를 알기 위함이다. 하지만 HDC는 포인터가 아니며, 항상 실제 객체의 메모리 주소를 가르킬 뿐이다. 물리적으로 메모리 주소가 바뀌어도, DC의 실제 위치를 찾아간다.

 

Reference


[1] https://m.blog.naver.com/winterwolfs/10165689957

단축키


1. gg // 맨 위로 이동

 

2. Shift + v + g // 전체 선택

 2.1 Shift + v // 라인 한 줄 선택

 

3. y // 복사

 

4. d // 삭제 

 

5. dd // 한 줄 삭제

발생 원인


해당 에러는 유니코드와 멀티바이트의 차이로 인해 발생하는 에러이다.

 

먼저 배경을 설명하자면, 대표적인 ASCII 코드의 경우 1Byte의 크기를 차지한다. 하지만 ASCII 코드만으로 다른 문자 체계를 전부 표현할 수 없다는 한계가 있다. 이로 인해 2Byte 문자 집합의 구성이 필요해졌으며 멀티 바이트를 2Byte 문자 집합으로 구성하고 ISO-2200에 멀티바이트로 정의하게 되었다. 

 

멀티바이트는 유니코드와 같이 2Byte 이상의 문자 집합이나, 표준으로서 합의가 이루어지지 않은 윈도우에서 개발된 코드이기 때문에 차이가 있게 된다. 이에 따라 기본적으로 Visual Studio에서 개발 시 유니코드를 사용할 것인지 멀티바이트를 사용할 것인지에 대한 기본 값을 설정한 뒤 개발을 진행해야 한다.

 

유니코드 문자열을 사용하려면 문자열에 L을 붙여 사용하면 된다.

ex) 유니코드    : (L"Hello World")

ex) 멀티바이트 : ("Hello World")  

 

유니코드로 개발된 프로그램을 멀티바이트로 포팅하려면 위와 같은 표현을 모두 수정해주어야 하는 불편함이 따른다. 이를 해결하기 위해 _T 매크로라는 것이 사용되며, 문자열을 따옴표로 감싼 뒤 유니코드에서 사용한 L 대신 _T를 사용하면 매크로가 알아서 개발 환경에 맞게 번형되어 컴파일 될 수 있도록 한다.

 

해결 순서


1. [프로젝트] → [속성]

 

2. [구성 속성] [고급] [문자 집합] [멀티바이트 문자 집합 사용]

 

 

3. 해결

지금까지 객체를 생성하고 객체의 멤버변수 초기화를 목적으로 InitMembers라는 이름의 함수를 정의하고 호출했다. 정보은닉을 목적으로 멤버변수들을 private로 선언했으니 이는 어쩔 수 없는 일이다. 

 

'생성자'를 이용하면 객체도 생성과 동시에 초기화할 수 있다.

 

생성자(Constructor)


class SimpleClass
{
    private:   
		int num;
    public:
    	SimpleClass(int n)		// 생성자 (constructor)
        {
			num = n;
        }
        
        int GetNum() const
        {
			return num;
        }
};

 

  • 클래스의 이름과 함수의 이름이 동일하다.
  • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.

 

위의 특징을 가지는 유형의 함수를 가리켜 생성자라 하며, 객체 생성시 딱 한번 호출되는 특징을 가지고 있다.

 

이전에 생성자를 정의하지 않았을 경우 다음과 같은 방식으로 객체를 생성했다.

 

SimpleClass sc;    // 전역, 지역 및 매개변수의 형태

SImpleClass * ptr = new SimpleClass;   // 동적 할당의 형태

 

생성자가 정의되었으니, 객체생성과정에서 자동으로 호출되는 생성자에게 전달할 인자의 정보를 다음과 같이 추가해야 한다.

 

SimpleClass sc(20);    // 생성자에 20을 전달

SimpleClass * ptr = new SimpleClass(30);    // 생성자에 30을 전달

 

  • 생성자도 함수의 일종으로 오버로딩이 가능하다.
  • 생성자도 함수의 일종으로 매개변수에 디폴트 값을 설정할 수 있다.

 

Reference


[1] 윤성우의 열혈 C++ 프로그래밍

 

int GetX() const;

int GetY() const;

void ShowRecInfo() const;

 

위에 선언된 const의 경우 "이 함수 내에서는 멤버변수에 지정된 값을 변경하지 않겠다"는 의미이다.

따라서 const 선언이 추가된 멤버함수 내에서 멤버변수의 값을 변경하는 코드가 삽입되면, 컴파일 에러가 발생한다. 따라서 이는 프로그래머의 실수를 최소화하기 위한 매우 의미 있는 선언이다. 

 

class SimpleClass
{
	private:
    	int num;
    
    public:
    	void InitNum(int n)
        {
			num = n;
        }
        
        int GetNum() // const 선언이 추가되어야 아래의 컴파일 에러 소멸
        {
        	return num;
        }
        
        void ShowNum() const
        {
        	cout << GetNum() << endl; // 컴파일 에러 발생
        }
};

 

위 클래스 정의에서 ShowNum 함수는 const 함수로 선언되었다. 그리고 실제로 함수 내에서는 멤버변수 num의 값을 변경하지 않는다. 그럼에도 불구하고 GetNum 함수를 호출하는 문장에서 컴파일 에러가 발생한다.

 

그 이유는 const 함수 내에서는 const가 아닌 함수의 호출이 제한되기 때문이다.

 

C++에서는 const 참조자를 대상으로 값의 변경 능력을 가진 함수의 호출을 허용하지 않는다. (실제 값의 변경여부에 상관이 없음). 따라서 const 참조자를 이용해서는 const 함수만 호출이 가능하다.

 

Reference


[1] 윤성우의 열혈 C++ 프로그래밍

정보은닉


먼저 정보은닉이란, 멤버변수를 private로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버 변수의 접근을 유도하는 것이 '정보은닉'이며, 이는 좋은 클래스가 되기 위한 기본조건이 된다.

 

int GetX() const;

bool SetX(int xpos);

 

int GetY() const;

bool SetY(int ypos);

 

위를 가리켜 액세스 함수라 한다. 이들은 멤버변수를 private로 선언하면서 클래스 외부에서의 멤버변수 접근을 목적으로 정의되는 함수들이다. 하지만 이들 함수는 정의되었으되 호출되지 않은 경우도 많은데 이는 지금 클래스의 정의과정에서 필요하진 않지만, 필요할 수 있다고 판단되는 함수들도 더불어 멤버에 포함시키는 경우도 많기 때문이다.

 

캡슐화


캡슐화가 무너지면 객체의 활용이 매우 어려워진다. 뿐만 아니라, 캡슐화가 무너지면 클래스 상호관계가 복잡해지기 때문에 이는 프로그램 전체의 복잡도를 높이는 결과로 이어진다.

 

캡슐화의 경우 어려운 개념에 속하며 이는 캡슐화의 범위를 결정하는 일이 쉽지 않기 때문이다.

 

경험이 많은 객체지향 프로그래머를 구분하는 첫 번째 기준은 캡슐화가 된다. 캡슐화는 일관되게 적용할 수 있는 단순한 개념이 아니고, 구현하는 프로그램의 성격과 특성에 따라서 적용하는 범위가 달라지는, 흔히 하는 말로 정답이란 것이 딱히 없는 개념이기 때문이다.

 

캡슐화에는 정보은닉이 기본적으로 포함된다.

 

Reference


[1] 윤성우의 열혈 C++ 프로그래밍

키워드 struct를 대신해서 class를 사용하면, 구조체가 아닌 클래스가 된다. 즉, 아래의 코드는 클래스의 정의이다.

class Car
{
    char gamerID[CAR_CONST::ID_LEN];
    int fuelGauge;
    int curSpeed;
    
    void ShowCarState() { . . . . }
    void Accel() { . . . . }
    void Break() { . . . . }
};

 

유일한 차이점이라고 하면, 문자 "struct"가 아닌 "class"를 사용한 것이다. 하지만 위와 같이 키워드를 바꿔놓을 경우 아래와 같이 구조체 변수를 선언하지 못한다.

 

Car run99 = {"run99", 100, 0};	(X)

 

그 이유는 클래스 내 선언된 함수가 아닌, 다른 영역에서 변수를 초기화하려 했기 때문이다. 클래스는 별도의 선언을 하지 않으면 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근가능하다. 따라서 다음과 같은 형태로 클래스 변수를 선언해야 한다.

 

Car run99;	(O)

 

클래스 내에 선언된 변수의 경우 기본적으로 클래스 내에 선언된 변수에서만 접근이 가능하다. 접근과 관련하여 별도의 선언을 하지 않을 경우 클래스 내에 선언된 변수 및 함수에 대한 접근은 허용하지 않는다. 따라서 이를 허용하기 위해

 

클래스는 정의를 하는 과정에서 각각의 변수 및 함수의 접근 허용범위를 별도로 선언해야 한다. 이것이 키워드 struct를 이용해서 정의하는 구조체와 키워드 class를 이용해서 정의하는 클래스의 유일한 차이점이다.

 

class에서 접근 제어에 사용되는 접근제어 지시자의 경우 총 세가지가 존재한다.

  • public : 어디서든 접근허용
  • protected : 상속관계에 놓여있을 때, 유도 클래스에서의 접근허용
  • private : 클래스 내(클래스 내에 정의된 함수)에서만 접근허용

 

구조체 또한 클래스와 마찬가지로 지시자의 선언이 가능하고, 그 의미도 같다. 다만 접근제어 지시자를 선언하지 않았을 경우 클래스는 private로, 구조체는 public으로 선언한다.

 

Reference


[1] 윤성우의 열혈 C++ 프로그래밍

+ Recent posts