닷넷 API

 

 

 

C#이 사용할 수 있는 API 집합체인 닷넷에는 굉장히 많은 API가 있다.

 

 

◎ 닷넷 API 탐색기와 Docs

 

마이크로소프트는 닷넷 API 탐색기를 제공하여 웹에서 모든 API 검색을 할 수 있다.

https://docs.microsoft.com/en-us/dotnet/api/

위의 링크를 들어가보면 API를 검색할 수 있다. 

 

닷넷에서 제공하는 대부분의 API는 클래스이다.

그리고 구조체, 열거형이 있고, 이러한 클래스, 구조체, 열거형을 특정 이름으로 묶어 관리하는 네임스페이스가 있다. 

 

* 클래스 (Class)

: Console 클래스, String 클래스 등 거의 대부분이 클래스이다. 

 

* 구조체 (Struct)

: DateTime 구조체, TimeSpan 구조체 형태로 표현하며, 클래스와 거의 동일하게 사용한다.

 

* 열거형 (enumeration)

: Color 열거형 등이 있으며, 특정 목록을 관리할 때 편리하다.

 

* 네임스페이스 (namespace)

: System 네임스페이스처럼 많은 양의 클래스와 구조체, 열거형을 묶어서 관리한다. 

 

 

위의 API 탐색기에서 Math를 검색했을 때 System.Math를 클릭해보면 기능들을 더 자세히 볼 수 있다. 

 

 

 

 

참고 자료;

C# 교과서 (저; 박용준 출판사; 길벗) 

 

 

어댑터

Adapter

 

 

 

어댑터 패턴은 다른 이름으로 Wrapper라고 불리기도 하며 인터페이스가 호환되지 않는 클래스들을 함께 이용할 수 있도록 타 클래스의 인터페이스를 기존 인터페이스에 덧씌우는 패턴이다.

이미 만들어진 클래스를 새로운 인터페이스에 맞게 개조시킬 때 사용하거나 기존 클래스를 개조해 필요한 클래스를 만들 때 사용한다. 

 

 

어댑터 패턴의 구현에는 두 가지 종류가 있다. 

◎ 객체 (Object) Adapter 패턴과 클래스 (Class) Adapter 패턴

 

출처; 위키/어댑터 패턴

1. 객체(Object) : 위임을 사용한 객체 어댑터 패턴

어댑터가 변환하려고 하는 클래스의 인스턴스를 가지고 있다.

 

 

 

출처; 위키/어댑터 패턴

2. 클래스(Class) : 상속을 사용한 클래스 어댑터 패턴

어댑터가 여러개의 다형성 인터페이스를 사용한다.

여러 개의 인터페이스를 상속하거나 구현해서 어댑터가 생성된다. 

 

 

 

◎ 장/단점

  객체 어댑터 클래스 어댑터
장점 상속이 아닌 구성(Composition)을 사용하기 때문에 더 유연 1. 어댑터가 Adaptee의 서브클래스이기 때문에 Adaptee의 행동을 오버라이드할 수 있다.
2. Adaptee 객체를 만들지 않아도 된다. 
단점 Adaptee 객체를 만들어야 사용 가능 1. 다중 상속이 지원되는 언어에서만 사용이 가능하다.
2. 상속을 이용하므로 한 어댑터 클래스가 특정 Adaptee 클래스에만 적용이 가능하다

 

 

 

 

 

 

참고 자료;

https://en.wikipedia.org/wiki/Adapter_pattern

https://arisu1000.tistory.com/27679

https://invincibletyphoon.tistory.com/20

https://ansohxxn.github.io/design%20pattern/chapter13/

 

 

erase와 remove의 차이

erase, remove

 

 

 

한줄로 간단하게 말하자면 erase는 멤버함수이고 remove는 알고리즘이다.

 

◎ erase

컨테이너의 erase는 실제 객체를 제거한다.

하나를 지울 수 있는데 STL의 데이터 공간까지 다 지우는 것이다.

즉, iterator에 해당하는 하나의 요소만을 삭제하며 capacity가 감소한다.

erase는 iterator를 넘겨준다.

 

	vector<int> v;
	v.resize(10, 0);

	for (int i = 0; i < v.size(); ++i)
	{
		v[i] = i + 1;
	}

다음과 같이 1부터 10까지의 벡터가 있다고 가정한다. 

 

	auto iter = v.begin();
	v.erase(iter + 1); // index 1번값 = 2

	printVector(v);

iterator가 begin값에서 +1, 즉 2를 가리키고 있는 상태에서 erase시키면 다음과 같은 결과가 나온다. 

 

◎ remove

Algorithm의 remove는 그 요소를 덮어씌움으로써 지워진 것처럼 보이게 된다.

정확하게 말하면 지워야 할 원소를 뒤의 원소들을 하나씩 앞으로 당겨서 덮어 씌우는 것이다

erase는 capacity까지 감소되었지만 remove는 조건을 함께 넘기면 조건과 일치하는 원소들은 다 지워지는데 공간은 그대로 남아있고 데이터만 삭제 된다.

즉, 해당 조건에서 일치하는 모든 요소는 삭제하지만 capacity는 그대로이다. 

 

 

 

◎ erase + remove?

특정 조건에 해당하는 원소를 아예 지우려면 erase와 remove를 함께 사용하면 좋다.

remove는 제거되지 않은 마지막 원소를 가리키는 itertor를 리턴하기 때문에 

erase( remove(시작, 끝, value), 컨테이너의 end 반복자); 

의 형태로 사용해준다. 

 

 

'Study > STL' 카테고리의 다른 글

[STL] next_permutation & prev_permutation (순열)  (0) 2020.09.11

 

 

상태

FSM(Finite State machine) ; 유한 상태 기계

 

 

FSM이란 여러 제한된 상태가 존재하며 그 존재들이 특정 조건에 따라 상태를 전이하는 형태의 개념적 모델이다.

간단하게 말하자면 보스의 패턴 구현 시 어느 특정 조건을 달성해 공격을 할 수 있게 되는 상황을 만들었다면 IDLE -> ATTACK 으로 상태가 바뀌는 것도 간단한 FSM 구현에 속한다. 

키 입력 등을 통해 '입력'이나 '이벤트가' 현재 움직여야 하는 머신에 전달이 되면 그에 해당하는 상태를 머신이 갖게 되는 것이다. 

 

 

◎ FSM의 장점?

1. 가질 수 있는 '상태'가 한정되며 한 번에 '한 가지' 상태만을 갖는다. 

상태란 행동이나 현재의 기능을 뜻한다. 예를 들어 플레이어의 움직임에서 생각해보면 걷기, 달리기, 점프 등은 각각 상태인 것이다. 

여기서 동시에 두 가지 상태가 일어나지 않게 하기 위해 사용하는 것이 FSM이다.

 

 

2. 내부 구조와 구현이 쉽다

머신의 행동을 직관적으로 간단하게 구현할 수 있다. 

 

3. 오류의 수정이 용이하며 유연성이 있다. 

 

4. 각 상태에는 입력에 따라 다음 상태로 바뀌는 '전이'가 있다.

입력이 들어왔을 때 현재 상태에 해당하는 전이가 있다면 전이가 가리키는 다음 상태로 변경한다.

예를 들어 기본 IDLE 상태에서 Down 키를 누르면 엎드리기 상태로 바뀌지만 점프 상태에서 Down키를 누르면 내려찍기로 전이한다.

 

 

 

 

◎ FSM의 단점

1. 규모가 커지면 설계가 복잡해 진다.

 

2. 제한된 범위의 문제에만 적용이 가능하다.

 

 

 

 

 

참고 자료;

더 빠르고 깔끔한 게임 코드를 구현하는 13가지 디자인 패턴 게임 프로그래밍 (저; 로버트 나이스트롬 , 역; 박일, 출판사; 한빛미디어)


https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=debuff9710&logNo=221333379009 

 

'Study > 디자인패턴' 카테고리의 다른 글

[디자인패턴] 어댑터 (Adapter)  (0) 2021.10.12
[디자인패턴] 싱글톤(Singletone)  (0) 2020.10.23

 

 

싱글톤

Singletone

 

 

 

싱글톤은 오직 한 개의 인스턴스만을 갖도록 보장해주며 해당 인스턴스를 전역에서 접근할 수 있는 메서드를 제공한다는 점에서 큰 장점을 제공한다. 

 

 

◎ 싱글톤을 사용하는 이유

1. 한 번도 사용하지 않으면 아예 인스턴스 자체를 생성하지 않는다.

처음 사용될 때 초기화되므로 사용되지 않으면 아예 초기화 되지도 않는다.

 

2. 런타임에 초기화된다.

애초에 전역으로 접근하기 위해 정적 클래스를 사용하기도 하지만 정적 멤버 변수는 컴파일러가 main 함수를 호출하기 전에 초기화하기 때문에 프로그램 실행 후에 알 수 있는 정보들은 활용할 수 없다.따라서 싱글톤을 이용하면 1번과 같이 사용할 때 초기화가 되므로 원하는 시점에서 초기화 하여 사용할 수 있다.

 

3. 싱글톤을 상속할 수 있다.

C# 같은 경우는 제네릭 클래스를 이용해 싱글톤을 상속받아 사용하는 방식도 많이 사용한다. 

 

4. 메모리 낭비 방지

한번의 new로 고정된 메모리 영역을 얻어 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있다.

 

 

◎ 싱글톤의 문제는?

싱글톤의 전역에서 접근할 수 있다는 장점은 곧 단점이 되기도 한다.

 

1. 버그 관리가 힘들 수 있다.

만약 싱글톤으로 호출한 함수에 버그가 있으면 해당 함수 뿐 아니라 해당 함수를 호출한 모든 부분을 확인해야 한다.

 

2. 커플링을 조장한다.

커플링이란 결합도를 뜻한다. 코드는 다른 코드들과의 의존성이 낮을 수록 좋다. 하지만 커플링은 하나의 모듈이 다른 모듈의 내부 데이터를 직접 접근할 때 발생하며, 다른 부분의 내용을 수정할 수 있게 된다. 

싱글톤을 이용하면 코드를 짜면서 나도 모르게 의존성이 높은 코드를 짜게 된다. 

 

3. 멀티스레딩 같은 동시성 프로그래밍에 알맞지 않다

무언가를 전역으로 만들면 모든 스레드가 보고 수정할 수 있는 메모리 영역이 생기게 되는 것이다. 다른 스레드가 전역 데이터에 무슨 작업을 하는지 모를 때도 있다. 그러다 보면 교착상태, 경쟁상태 등 찾기 어려운 스레드 동기화 버그가 생기기 쉽다. 

 

 

 

 

참고 자료;

더 빠르고 깔끔한 게임 코드를 구현하는 13가지 디자인 패턴 게임 프로그래밍 (저; 로버트 나이스트롬 , 역; 박일, 출판사; 한빛미디어)

 

 

Permutation

순열

 

 

 

◎ 수학에서 순열 구하기

n개에서 r개를 택하는 순열이란 서로 다른 n개의 원소에서 r개를 중복없이 골라 순서에 상관있게 나열하는 것이다. 

여기서 중요한 것은 '순서 있게'다. 왜냐하면 개념이 헷갈리는 조합은 '순서'가 없이 나열하는 것이기 때문에 구분을 잘 해야 한다. 

표현은 주로 nPr로 한다. (n개에서 r개를 택하는 Permutation)

 

이 순열을 구하는 방법은 간단하다. 바로 팩토리얼을 이용하면 된다.

 

nPr = n x (n-1) x (n-2) x ... x (n-r+1)

 

식을 간단하게 표현하면 위와 같다. 

 

 

◎ Permutation 사용

순열을 구하기 위해서는 재귀를 통해서 직접 구현할 수도 있다. 하지만 stl에 있는 기능을 가져다가 사용하면 빠르고 간단하게 순열을 만들 수 있다.

 

std::prev_permutation과 std::next_permutation은 현재 범위가 현재보다 더 작은 순열(이전 순열) 또는 더 큰 순열(다음순열)이 되도록 요소들을 정리한다. 이전 순열이나 다음 순열이 있으면 true, 없으면 false를 리턴한다

 

이때 순열이 끝나 false를 리턴하는 경우는 완전히 역순이 되는 경우다. 이 때 다시 원래 순서대로 바꾼뒤에 false를 리턴하는 것이다. 

ex) 1234의 경우 return 하기 직전 4321였다가 false를 리턴할때 다시 1234로 만들어준다. 

 

단 permutation은 중복이 있는 경우에는 중복을 제외하고 순열을 만들어준다. 

 

permutation 기능의 원리는 다음과 같다.

1. 우선 뒤에서부터 탐색을 시작한다. 오름차순의 형태를 띄는 구간이 나올 때까지 진행하며 작은 쪽은 i, 큰 쪽은 ii라고 가정한다. 

2. 그 다음에는 뒤에서부터 i보다 큰 j를 찾아 i와 j를 바꿔준다.

3. 그 후 ii를 포함해 뒷부분을 모두 reverse한다. 

 

 

범위를 이전 순열로 만드는 것

bool prev_permutation(BiIt first, BiIt last) 

 

 

범위를 다음 순열로 만드는 것

bool next_permutation(Bilt first, BiIt last)

 

 

 

위의 인자에 정렬 기준을 명시적으로 사용할 수 있다. 기본적으로는 std::less가 사용된다.

두 알고리즘 중 어떤 것을 사용하든, 주어진 범위의 모든 순열을 손쉽게 생성할 수 있다. 

 

 

 

ex ) permutation을 사용한 예시 

#include <algorithm>

std::vector<int> Init{1,2,3};
do{
	for(auto i:Init) std::cout << i;
	std::Cout << " ";
} while(std::next_permutation(Init.begin(), Init.end())); 

// 출력 예시 123 132 213 231 312 321 

std::reverse(Init.begin(), Init.end());
do{
	for(auto i:Init) std::cout << i;
    std::cout << " ";
 } while(std::prev_permutation(Init.begin(), Init.end()));
 
 // 출력 예시 321 312 231 213 132 123

 

 

 

permutation말고 직접 순열 구하기? 

더보기

 

참고 자료;

namu.wiki/w/%EC%88%9C%EC%97%B4


핵심 C++ 표준 라이브러리 (라이너 그림 지음; 류광 옮김. 인사이트) 


jeonggyun.tistory.com/110

'Study > STL' 카테고리의 다른 글

[STL] erase와 remove의 차이  (0) 2021.10.04

 

 

전처리기 지시문

#define , #error, #if / #elif / #else 및 #endif / #ifdef와 #ifndef

 

 

 

전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하기 위해 사용된다.

전처리기(=선행처리)란 컴파일 이전의 처리를 의미하고 소스파일은 컴파일러에 의해 컴파일 되기 이전에 이 전처리기 단계를 거친다.

 

 

 

◎ #define 지시문

이 #define 지시문은 세 부분으로 나뉜다.

#define PI 3.1415
지시자 매크로 매크로 몸체

많이 쓰이는 PI로 예시를 들어보면 코드에서 PI라는 이름(매크로)이 나오면 3.1415(매크로 몸체)로 바꾸라는 명령과 같다.

 

#define PI 3.1415

int function_before()
{
	int angle;
	angle = 180 * PI;
}

int funcint_after()
{
	int angle;
	angle = 180 * 3.1415;
}

 

전처리기의 단계를 거치면 코드 상에서 function_before의 내용을 function_after로 바꾼다.

이렇게 상수를 매크로로 정의한 것을 매크로 상수라고 부르고

상수가 아닌 함수로 정의해 놓은 것을 매크로 함수라고 부른다. 

 

 

 

 

◎ #if, #elif, #else 및 #endif 지시문 

#if... #endif는 조건부 코드 삽입을 위한 지시문이다. 

소스파일의 각 #if 지시문은 #endif 지시문과 일치해야 하며 #if와 #endif 사이에는 여러개의 #elif가 나올 수 있지만 #else는 하나만 사용할 수 있다.

 

#define ENABLE 1
#define IMPOSSIBLE 0


int main()
{
#if ENABLE
	cout << "ENABLE" << "\n";
#endif IMPOSSIBLE
	cout << "IMPOSSIBLE" << "\n";
}

 사용법은 이처럼 if문이 나오면 반드시 endif가 나와야 하며 ENABLE이 참(=1)이라면 cout << "ENABLE" << "\n"; 의 코드를 삽입하고 IMPOSSIBLE이 참이라면 cout << "IMPOSSIBLE" << "\n";의 코드를 삽입하라는 뜻이다. 

 

 

 

 

 

 

◎ #ifdef 및 #ifndef 지시문

정의 된 연산자와 함께 사용될 때 #if 지시어와 동일한 효과를 가진다.

 

#ifndef 식별자
#ifndef 식별자

이 지시문은 아래의 지시문과 같은 내용이다.

#if 정의된 식별자
#if! 정의된 식별자

 

#if는 매크로가 참인지 거짓인지에 따라서 동작한다면 #ifdef는 매크로가 '정의되어 있는지 아닌지'를 기준으로 동작한다. 

#ifdef는 매크로가 정의되어 있다면이고

#ifndef는 매크로가 정의되어 있지 않다면이라는 의미이다. 

 

 

 

 

◎ #error 지시문

전처리기가 해당 지시문을 발견하면 컴파일이 중지되고 여기에 포함된 오류 메시지가 전달된다. 

#define _EXIT

#ifdef _EXIT
#error "Error exists."
#endif

 

전처리기가 해당 코드를 만나게 되면 error.c : error: #error "Error exists." 이렇게 출력되며 종료된다. 

 

 

 

 

 

이 외에도 #import, #include, #line, #undef, #using 등이 전처리기 지시문에 포함된다. 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/ko-kr/cpp/preprocessor/preprocessor-directives?view=vs-2019

 

전처리기 지시문

전처리기 지시문Preprocessor directives 이 문서의 내용 --> #define, #ifdef과 같은 전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하는데 사용됩니다.Preprocessor directives, such as #define and #ifdef, are typically used to make source programs easy to change and easy to

docs.microsoft.com

 

 

 

클래스(Class)와 구조체(Struct)의 차이

구조체에 대한 설명과 클래스와 구조체의 차이

 

 

 

연관있는 데이터를 하나로 묶는다는 기본 개념은 동일하다. 그러면 각각은 무엇이고 둘의 차이는? 

 

◎ 구조체

C++에서 구조체를 선언하는 방법

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

 

 

C++에서는 C에서 처럼 typedef를 추가할 필요 없이 사용이 가능하다.

int main()
{
	My_S info = {"hi", 90, 100}
	
	info.name = "hello";
	info.age = 100;
	info.number = 7;
}

 

 

구조체와 클래스를 비교하면서 구조체에는 함수를 추가하지 못한다는 이상한 내용도 가끔 보이는데 아니다.

C++에서는 구조체 안에도 함수 추가 가능하다

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age)
	{
		age = _age;
	}
}

 

여기서 내가 My_S 구조체의 변수를 여러 개 선언해서 메모리 공간에 구조체 변수가 할당이 될 때, 

함수는 모든 구조체 변수 내에 각각 별도로 존재하는 것이 아니다.

구조체 안의 함수는 한 번만 선언된 하나의 함수를 공유한다. 

 

 

그리고 물론 이 함수를 외부로 뺄 수도 있다.

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age);
}

void My_S::SetMyAge(int _age)
{
	age = _age;
}

 

함수 원형 선언은 구조체 안에 두고 정의를 밖으로 빼서 정의하는 것이다.

딱히 쓸 일이 없어 보이지만 구조체를 보는 순간 정의와 기능을 한 눈에 파악하기 쉽게 작성하는 데 도움이 되기도 한다. 

 

그런데 구조체 안에 함수가 정의 되어있을 때와 바깥에 있을 때 다른 점이 하나 존재한다.

바로 구조체 안에 있을 때는 함수가 inline으로 처리되고 있다는 점이다. 

인라인의 의미를 그대로 유지하고 싶다면 inline 키워드를 사용해서 명시적으로 지시하면 된다. 

 

 

 

 

 

◎ 클래스

구조체는 클래스의 일종이다.

struct를 사용하면 구조체, class를 사용하면 클래스가 되는 것이다.

Class My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

위의 구조체와 비교했을 때 기본 선언 시 사용하는 키워드만 다를 뿐이다.

 

 

그런데 이 키워드의 차이는 초기화의 방법의 차이를 발생시킨다.

int main()
{
	My_S info = {"hi", 90, 100};
}

struct는 이와같은 방법으로 초기화가 가능했지만 클래스에서는 불가능하다.

그 이유는 클래스 내에 선언된 함수가 아니고 다른 영역에서 초기화를 하려고 했기 때문이다. 

클래스는 기본적으로 별도의 선언이 없다면 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근이 가능하다. 

 

여기서 접근에 대한 구분이 존재한다. 그것을 접근제어 지시자라고 하는데

public 모두에게 공개; 아무나 접근 가능
protected 상속관계로 얽혀있는 클래스에서만 접근허용
private 클래스 안에서만 건들일 수 있다.

 

접근제어 지시자를 명시하지 않으면 class는 기본으로 private으로 설정한다. 

그래서 위의 class는 private으로 지정되어 있어 외부에서 접근이 불가능한 것이다. 

 

 

이 접근제어 지시자는 구조체에서도 선언이 가능하다.

단, 클래스에서는 따로 지정하지 않으면 private으로 설정이 되지만 구조체에서는 public으로 설정이 된다. 

 

 

 

 

 

 

 

 

 

참고 자료;

윤성우의 열혈 C++ 프로그래밍 ( 저; 윤성우, 출판사; 오렌지 미디어)

'Study > C++ , C#' 카테고리의 다른 글

[C#] 닷넷 API  (0) 2021.12.22
[C++] 전처리기 지시문  (0) 2020.01.19
[C++] QueryPerformanceCounter / 프로그램 실행 시간 측정  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10

+ Recent posts