2019. 10. 4. 18:24

 

안녕하세요. 게임개발자 놀이터 입니다.

 

이전 포스팅에 이어 웹크롤링에 대해서 포스팅 하고자 합니다

 

웹에서 정보 가져오기 - 1

 

저번에 requests 설정까지 마무리 했습니다.

 

그럼 이제 requests가 정상 작동하는지 확인해봅시다.

 

 

이와 같이 소스를 입력 한 후 빌드 ( 컨트롤 + 쉬프트 + B ) 를 해봅시다.

 

 

status_code 값과 OK 값이 200, true가 나온다면 성공입니다.

 

하지만 html 텍스트는 현재 읽을 수 없습니다.

 

html을 읽기 위해선 다음과 같은 세팅이 필요합니다.

 

BeautifulSoup

 

html의 내용을 파이썬에서 사용할 수 있도록 파싱 해주는 패키지 입니다.

 

명령 프롬프트 창을 이용해 bs4를 설치 해줍니다.

 

 

 

이제 html을 파싱할 수 있습니다.

 

 

위처럼 소스를 입력하고 빌드하시면 HTML 소스가 로그창에 출력 된걸 확인 하실 수 있습니다.

 

데이터 검색 

 

테스트에 사용 할 HTML  URL입니다. :  https://www.google.com/search?q=+site:tistory.com+%EC%8B%9C%EB%A6%AC%EC%8B%9C%EC%95%88&sa=X&ved=2ahUKEwjuofzmjoLlAhWUE4gKHS-wAYEQrAN6BAgGEBQ&biw=2048&bih=1042

구글 페이지에서 [site:tistory.com 시리시안] 을 검색한 URL입니다.

 

 

 

이곳에서 저는 검색 제목만 모아서 크롤링 해보려 합니다.

 

크롬에서는 검사 기능을 통해 바로 HTML을 볼 수 있습니다.

 

찾고자 하는 글에 우클릭 후 검사를 눌러 줍니다.

검사를 누르면 우측에 HTML 창이 뜨게 됩니다.

 

 

내용을 보니 저 검색결과는 div 태그에 class 'ellip'를 가지고 있군요. 이 정보를 이용해 파싱해 찾아 봅시다.

 

 

전체 소스입니다. 중간에 잘린 url은 위에 적용된 HTML입니다.

 

빌드 해보니 아무런 결과가 나오지 않았습니다. 아무래도 클래스 명칭 'ellip' 로 찾는게 문제 인거 같네요.

 

정확한 HTML 비교를 위해 prettify() 로 HTML을 출력 해봤습니다.

 

클래스 명칭이 다르네요, 아무래도 검색할때 생성되는 구조이거나 랜덤으로 만들어지는거 같습니다. 따라서 클래스 비교 방식은 사용 할 수 없겠군요.

 

이번엔 글 제목을 클릭해서 들어 갈 수 있으니, a 태그로 찾은 후 div로 분류해봤습니다.

 

 

출력되는 list2를 확인해보니 아래와 같았습니다.

 

 

첫번째 검색결과와 제가 필요했던 애들의 클래스 명칭이 전부 똑같네요. 이건 이용할 수 있을꺼같습니다.

 

 

첫번째 클래스 명칭을 이용해 찾도록 수정했습니다.

 

 

제가 얻고자 했던 목록들만 얻을 수 있게 되었습니다.

 

이처럼 크롬의 HTML을 분석하여 원하는 정보를 찾을 수 있습니다. 구글의 경우 위와 같았지만, 다른 사이트의 경우엔 분석이 우선시 되니, HTML 을 읽는 기술이 필요할꺼 같습니다.

 

감사합니다.

 

 

Posted by 시리시안

댓글을 달아 주세요

  1. Favicon of http://tvple.me/ BlogIcon 티비다시보기 2020.07.03 16:55  댓글주소  수정/삭제  댓글쓰기

    잘 보고 갑니다~~

2019. 10. 4. 16:45

 

안녕하세요. 게임개발자 놀이터 입니다.

 

웹크롤링에 대해서 포스팅 하고자 합니다

 

웹 크롤러

 

우리가 어떤 정보를 웹 브라우저로만 보는 것이 아니라, 그 정보 들을 내가 이용하기 편한 방식(XML, JSON) 등으로 DB에 쌓아두고 싶을 때가 있습니다.

웹 데이터를 원하는 방식으로 가공하여 저장하는 기능을 웹크롤링 한다고 합니다.

 

웹에서 정보 가져오기 - 1

 

php 의 requests 라는 라이브러리를 이용해서 웹의 정보를 가져옵니다.

 

설치하기 위해선 pip를 이용합니다.

 

[Window+R]  + cmd 를 이용해 명령 프롬프트를 엽니다.

 

사용자 PC에 php가 설치되어 있는 폴더로 이동합니다.

 

폴더 안에 Scripts 폴더까지 이동합니다.

 

저의 경우는 C:\Python27\Scripts 입니다.

 

그후, pip install requests를 입력합니다. 아래 화면 처럼 뜬다면 성공입니다.

(pip 업그레이드 문구는 무시해주세요 )

 

 

제대로 설치 되었는지 확인을 위해 ATOM 에디터로 넘어와 임포트 해봅니다.

 

ATOM Python 모듈 설정

 

ATOM에서 pip로 설치한 모듈을 사용하려면 추가로 설정해줘야 합니다.

 

우리는 '파이썬 프로그래밍 -01 ATOM 설치 (Python)' 에서 ATOM 플러그인으로 'script'를 설치 했습니다.

 

모듈을 내장하여 빌드 하기 위해선 'script'플러그인은 조금 수정 해줘야 합니다.

 

우선 [컨트롤 + ,]을 눌러 settings 창을 엽니다.

그후, 좌측 Packages 메뉴를 누른후 Installed Packages 에서 'script'를 검색한 후 Settings를 눌러 줍니다.

 

그러면 'script'의 설정 창이 열리게 되는데, [View Code]를 눌러 줍니다.

 

ViewCode를 누르면 아톰창이 새로 열리게 됩니다.

 

그후 왼쪽 프로젝트 목록에서

 

[ script / lib / grammars / python.coffee ]를 열어 줍니다.

 

 

 

python 으로 되어있는 경로를 각각 파이썬 경로로 수정해줍니다.

 

저의 경우는 [ C:/Python27/python ] 입니다.

 

 

이후 창을 닫은 후, 메인 소스에 requests를 임포트 하는 코드를 작성 한 후 빌드 해보면

 

 

 

아무런 에러 없이 완료 된다면 성공입니다.

 

감사합니다.

 

이후 내용은 다음 포스팅에서 진행 하겠습니다.

 

 

 

 

 

 

 

 

 

 

 

Posted by 시리시안

댓글을 달아 주세요

2019. 10. 4. 15:45

 

안녕하세요. 게임개발자 놀이터 입니다.

 

ATOM으로 python 프로그래밍 중 에러가 발생하여 해결법을 포스팅 하려합니다.

 

먼저 에러 로그는 다음과 같습니다.

 

UnicodeEncodeError: 'cp949' codec can't encode character '\xa0' in position 63430: illegal multibyte sequence

 

유니코드 관련 에러로 확인됩니다.

 

ATOM의 우하단을 확인해봅시다.

 

 

해당 인코딩 방식이 UTF-8 인지 확인합니다.

 

만약 UTF-8이 아니라면, UTF-8로 변경 후 다시 시도해봅니다.

 

이미 UTF-8 이거나, 에러가 계속 된다면

 

 

해당 소스를 최상단에 추가 해주면 됩니다.

 

감사합니다

 

 

Posted by 시리시안

댓글을 달아 주세요

2019. 10. 4. 12:08

안녕하세요. 게임개발자 놀이터 입니다.

 

이번에 웹 크롤링을 해야 하는 이슈가 생겼는데, 기존 언어보다 Python이 강력하다는 이야기를 듣고, 파이썬으로 진행 하며 정리 해보려합니다.

 

먼저 파이썬 프로그램을 만들고 편집 할 수 있는 'ATOM Text Editor' 설치입니다.

 

아톰은 텍스트 에디터 기능 뿐만이 아닌 패키지 설치로 파이썬, C, Nodejs 등 여러 언어를 지원하여 상당히 편리합니다.

 

이제, 아톰을 설치해 보도록 하겠습니다.

 

먼저 아톰 공식 웹사이트에 들어갑니다.

 

 

윈도우 1.40.1 버전 다운로드를 클릭합니다.

 

클릭시 자동으로 다운로드가 시작됩니다. 적은 용량으로 바로 다운로드가 완료됩니다.

 

 

클릭시 아톰 특유의

 

 

우주선의 짐을 싦는 모습이 나옵니다. 조금만 기다리시면 자동으로 실행됩니다.

 

아톰이 설치 완료되면 화면 중앙에 아래와 같은 메뉴가 보입니다.

 

먼저 패키지 설치를 위해 아래 버튼을 클릭해줍니다.

 

 

그 후 파이썬 설치를 위해 autocomlite-python을 검색해 설치 해줍니다.

 

 

아래와 같이 자동생성 기능이 포함되어 있어 상당히 편리 합니다.

 

 

다시 인스톨 화면으로 돌아와서 이번엔 빌드를 위한 script를 검색해서 설치해줍니다.

 

여러 소스를 빌드할 수 있도록 해주는 패키지입니다.

 

 

 

설치가 완료된 후, 우측 프로젝트를 우클릭하여 New File 하여 파일을 하나 생성해줍니다.

 

 

확장자 (.py)을 입력하지 않으면, 파이썬 파일로 인식하지 않습니다. 꼭 입력해주세요.

 

 

해당 파일에 아래처럼 print문을 입력한 후

 

상단 메뉴에서 Packages - Script - Run Script ( 단축키 Ctrl + Shift + B ) 를 누르시면 소스가 실행됩니다.

 

하단 로그에 아래와 같이 글이 뜬다면 성공입니다.

 

 

이로써 파이썬 환경 개발을 위한 기초 세팅이 끝났습니다.

 

제가 잘 아는 부분이 아니라, 개발하면서 나온 화면을 촬영후 글을 작성했습니다. 댓글 달아주시면 최대한 응답해드리겠습니다.

 

감사합니다.

 

 

Posted by 시리시안

댓글을 달아 주세요

2017. 4. 6. 13:21

안녕하세요. 게임 개발자 놀이터 입니다.


C#에서 자주썻던 이벤트 콜백 함수를 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
25
class Button : public CSprite
{
public:
    Button();
    ~Button();
 
private:
    typedef std::function<void()> Event; //인수가 없는것
 
    short state;
    Event OnClick;
    Event OnMouseOver;
    Event OnMouseOut;
 
public:
 
    void SetOnClick(Event Function);
    void SetOnMouseOver(Event Function);
    void SetOnMouseOut(Event Function);
 
    virtual void MouseEvent(DIMOUSESTATE MouseState) override;
 
 
 
};
cs


여기서 상속받는 CSprite와 가상함수 MouseEvent는 무시하셔도됩니다. ( 제가 만든 엔진에 일부입니다..)


다음은 소스 코드입니다.


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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Button::Button()
{
    state = 0;
    OnClick = NULL;
    OnMouseOver = NULL;
    OnMouseOut = NULL;
}
 
 
Button::~Button()
{
}
 
void Button::SetOnClick(Event Function)
{
    OnClick = std::move(Function);
}
void Button::SetOnMouseOver(Event Function)
{
    OnMouseOver = std::move(Function);
}
void Button::SetOnMouseOut(Event Function)
{
    OnMouseOut = std::move(Function);
}
 
void Button::MouseEvent(DIMOUSESTATE MouseState)
{
    CPoint mouse = Director::GetInstance()->Get_Mouse();
 
    if (this->GetBoundingBox().containsPoint(mouse) && state != 1)
    {
        //MouseOver
        if (OnMouseOver != NULL)
            OnMouseOver();
        state = 1;
    }
    else if (!this->GetBoundingBox().containsPoint(mouse) && state != 0)
    {
        //MouseOut
        if (OnMouseOut != NULL)
            OnMouseOut();
        state = 0;
    }
    else if (this->GetBoundingBox().containsPoint(mouse) && state == 1)
    {
        //MouseClick
        if (MouseState.rgbButtons[0& 0x80)
        {
            if (OnClick != NULL)
                OnClick();
            state = 2;
        }
    }
}
cs



음.. 소스는 딱히 볼게 없군요!


std::move로 넣어줘야 한다는것 정도!?


그럼 적용법을 알아볼까요.


1
2
3
4
5
6
7
8
9
10
11
12
void CTestScene::TestFunction()
{
    CCLog("버튼 테스트 함수 호출!");
}
 
void CTestScene::ButtonInit()
{
    ButtonTest = new Button();
    ButtonTest->SetOnClick(std::bind(&CTitleScene::TestFunction, this));
 
    m_default_Layer->AddChild(ButtonTest);
}
cs


여기서 중요한 부분은 9번째줄이네요


std::bind로 넘겨주는것입니다.



음.. 뭐랄까.. 무언가 만들었다기보단 있는 기능을 사용한거라 글을 적기 애매하네요.


질문은 댓글로 부탁드려요

Posted by 시리시안

댓글을 달아 주세요

2017. 3. 29. 19:21

안녕하세요. 게임 개발자 놀이터입니다.


이번 포스팅에서는 변수 선언시 저장되는 영역을 적어보려고합니다.


크게 4개 또는 3개의 영역이라 표현하는데요


순서대로 


텍스트, 데이터, 스택(Stack), 힙(Heap)


입니다. 또는 3가지로 나눌땐 텍스트와 데이터를 하나로 합쳐서 보기도 한다네요.


그럼 순서대로 적어보겠습니다.


텍스트 영역

프로그램의 실행 코드가 존재하는 영역입니다.

 즉, 내가 작성한 코드들이 이곳에 위치합니다.


텍스트는 비교적 간단하네요


데이터 영역

    • 전역 변수나 정적 변수(static)가 이곳에 기록됩니다.
    • 데이터 영역내에서도 3가지로 분류가 가능합니다
      • 읽기 전용 영역
        • Const나 상수 문자열 등이 이곳에 속합니다.
      • 읽기/쓰기 영역
        • 전역 변수중 초기화가 된 변수들입니다.
      • 초기화가 되지 않은 영역 (BSS (Block Started by Symbol))
        • 전역 변수이면서 초기화가 되지 않고 선언되거나 static으로 선언된 변수들입니다.


스택 영역(Stack)

    • 지역 자동 변수 (지역 auto변수) 입니다.
    • 메모리를 따로 설정하지않아도 함수 시작(스코프영역 진입)에 자동으로 생성되고 함수 끝(스코프영역 벗어남)에 자동으로 파괴됩니다.
    • 스코프 영역안에 잡힌 주소값을 가리키는 포인터 변수도 auto변수 이기 때문에 이곳에 위치합니다.
      • (단, 주소값을 가르킬뿐, 주소값의 영역은 다릅니다.)


힙 영역(Heap)

    • 일반적으로 malloc 처럼 동적으로 메모리를 할당받을때 이곳의 영역이 사용됩니다.
    • 이곳은 자동 파괴가 되지 않습니다.



이렇게 정리하고 보면 되게 간단한 4가지인데, 항상 필요할때 기억이 나질 않네요.


읽어주셔서 감사합니다.


Posted by 시리시안

댓글을 달아 주세요

2016. 12. 12. 15:19

안녕하세요. 게임 개발자 놀이터입니다.


이번 포스팅에선 C#에서 C++에서 비교적 자주 쓰이는 (저는 편해서 자주 쓰는..) typedef를 C#에서 사용하는 방법입니다.


최근 C#을 사용하면서 비교적 KeyValuePair를 되게 자주 사용하게 됬습니다.


특정 2개의 값을 묶어서 관리하기 위해서였죠. 근데 KeyValuePair가 많아지면 어느게 어떤건지 변수명만으로 구분하기가 애매해 질때가 있었습니다.


그때 생각난게 C++의 typedef 였죠.


이름을 바꿔주면 편하지않을까..


생각하면서 typedef를 입력해봤으나 역시나 안됬습니다.


다른 방법이 없을까 찾아보면서 MSDN에 들어가게 됬습니다 (링크)


링크에 적힌 원문을 그대로 가져오면


typedef 키워드입니다. C++에서 typedef는 짧게 만들어지거나 이미 선언된 형식에서 좀 더 편리한 이름으로 사용됩니다. C#의 경우 using 지시문이 이 기능을 제공합니다.


라고 합니다.


그럼 예제 코드를 볼까요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Silisia
{
    using indexValuePair = KeyValuePair<intint>;
    public class ExampleClass
    {
        void test()
        {
 
            KeyValuePair<intint> Pair = new indexValuePair();
 
            indexValuePair Pairs = new indexValuePair();
        }
    }
}
cs


위 처럼 사용할수 있습니다.


무조껀 클래스 상단에 위치해야 합니다. 그렇지 않으면 에러를 뽑아내게 됩니다.


물론 최상단에 적어야 하는게 맞겠지만.. C언어가 생각나네요..


몇번 사용하다가 결국엔 새로 클래스를 만들어 사용했습니다.


그래도 참고가 될까 이곳에 글을 남깁니다.


감사합니다.


------------------------------


추가


C++도 템플릿 에일리어스(C++11)로 using 키워드를 사용할 수 있습니다.

기존 typedef 해석 방법은 구식(C와 호환)이라 템플릿 사용 시 문제가 있어서 C 코드와 호환이 필요하지 않다면 typedef 대신 using을 쓰는 걸 권장한다고합니다.


라고합니다. (링크로 남긴 페북을 통해 답글 중.)




Posted by 시리시안

댓글을 달아 주세요

2016. 12. 5. 13:15

안녕하세요. 게임개발자 놀이터 입니다.


이번 포스팅에선 C++의 예약어중 하나인 inline에 대해 포스팅 하려고합니다.


C++ 책을 보다보면 꼭 나오는 예약어중 하나인데요.


쉽게 말하자면, 인라인 함수는 컴파일된 함수의 코드가 프로그램의 코드안에 직접 삽입이 되는겁니다.


즉, 컴파일러가 함수를 호출하는 과정이 사라져 버리는것이죠.


코드를 볼까요?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
 
using namespace std;
 
inline void Inlinefuction()
{
    cout<<"*** Inlinefuction() ***"<<endl;
}
 
 
int main()
{
 
    Inlinefuction();

    return 0;
}
cs


이렇게 선언한 코드를 컴파일 하게되면


1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
 
using namespace std;
 
int main()
{
 
    cout<<"*** Inlinefuction() ***"<<endl;
 
    return 0;
}
cs


위와 같이 바뀌게 됩니다. 어떤가요?


좀더 확실한 결과를 보기 위해 어셈블리 코드로 확인 해보겠습니다.


Visual C++에서 어셈블리 파일을 생성할떄 /Ob1 이라는 추가 명렁 옵션을 주셔야 어셈블리 코드가 정상적으로 출력됩니다.

(진짜 몰라서 엄청 고생했네요...)


먼저 인라인이 적용 되지 않은 어셈블 코드중 메인 함수를 보면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
; 12   : {
 
    push    ebp
    mov    ebp, esp
 
; 13   :     Inlinefuction();
 
    call    ?Inlinefuction@@YAXXZ            ; Inlinefuction
 
; 14   :     return 0;
 
    xor    eax, eax
 
; 15   : }
 
    cmp    ebp, esp
    call    __RTC_CheckEsp
    pop    ebp
    ret    0
cs


위와 같이 나오게 됩니다. 그후 Inline이 적용된 함수의 어셈블 코드를 보면


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
26
27
28
29
30
31
32
; 12   : {
 
    push    ebp
    mov    ebp, esp
    push    esi
 
; 13   :     Inlinefuction();
 
    mov    esi, esp
    mov    eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
    push    eax
    push    OFFSET ??_C@_0BI@FMEKNHNP@?$CK?$CK?$CK?5Inlinefuction?$CI?$CJ?5?$CK?$CK?$CK?$AA@
    mov    ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    push    ecx
    call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
    add    esp, 8
    mov    ecx, eax
    call    DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    cmp    esi, esp
    call    __RTC_CheckEsp
 
; 14   :     return 0;
 
    xor    eax, eax
 
; 15   : }
 
    pop    esi
    cmp    ebp, esp
    call    __RTC_CheckEsp
    pop    ebp
    ret    0
cs


이렇게 나오게 됩니다.


13번째 줄을 보면 확연히 차이가 나는걸 보실수 있습니다.


음.. 보기 어렵군요?


함수를 좀더 보기 쉽게 바꿔보겠습니다.


소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
 
using namespace std;
 
int Inlinefuction(int a, int b)
{
    return a+b;
}
 
 
int main()
{
    int n;
    n = Inlinefuction(1,2);
    return 0;
}
cs


Inline이 적용하지 않은 어셈블리 코드

1
2
3
4
5
6
7
8
9
10
11
12
; 13   :     int n;
; 14   :     n = Inlinefuction(1,2);
 
    push    2
    push    1
    call    ?Inlinefuction@@YAHHH@Z            ; Inlinefuction
    add    esp, 8
    mov    DWORD PTR _n$[ebp], eax
 
; 15   :     return 0;
 
    xor    eax, eax
cs


Inline이 적용된 어셈블리 코드

1
2
3
4
5
6
7
8
9
10
; 13   :     int n;
; 14   :     n = Inlinefuction(1,2);
 
    mov    eax, 1
    add    eax, 2
    mov    DWORD PTR _n$[ebp], eax
 
; 15   :     return 0;
 
    xor    eax, eax
cs



14 번의 코드를 보면 확 차이가 느껴지실껍니다.

가장 크게 어셈블리 코드상에서 call이 불리지 않게됩니다.

또 지금 코드상에선 push도 호출되고 있지 않네요.



일반 함수는 호출되는 과정을 보면, 우선 매개변수를 스택이 집어넣고, 함수를 호출합니다. 그후 함수내 리턴값을 위한 스택의 push, pop 도있고, 리턴후 스택 정리를 위한 pop이 있습니다.

이러한 함수 호출은 스택에 접급 하기 때문에 즉, 메모리에 접근 하기 때문에 속도가 저하 될 수 있습니다.

함수 호출에 대한 자세한 이야기는 다른 포스팅에서 다루겠습니다.



인라인을 쓰게되면 가장 먼저 속도와 메모리 면에서 효과가있습니다.

메모리는 위에 적은 것처럼 함수를 부르기 위해 스택에 넣었다 빼는 과정이 없어지기 때문이고, 그에따라 속도 면에서 더 빨라진답니다.


하지만 단점은 너무 반복해서 사용하게 될경우 어셈블리 코드가 길어진다는 것입니다. push와 call로 끝나는 함수가 여러번 호출되면서 여러번의 내용이 다 복사되서 어셈블리 코드로 적히게 되면 그 길이는 장난 아니겠죠?



결론


음.. 제 짧은 지식으로 내린 결론은 

함수 구문이 짧고, 호출이 적은 함수는 inline화 시키자는 겁니다. 크게 영향을 주지않는 함수 호출을 줄이자는거죠..!!


잘 모르는 분야라 댓글로 지적 해주시면 감사하겠습니다.






Posted by 시리시안

댓글을 달아 주세요

2016. 11. 30. 13:51

안녕하세요. 게임개발자 놀이터입니다.


이번 포스팅에선 C++로 Swap 함수를 짜보려고합니다.


흔히 Swap 함수를 짠다고 하면 자료형 int에 한애 대부분 이렇게 짤것입니다.


1
2
3
4
5
6
void Swap(int *a, int *b)
{
    int temp = *b;
    *= *a;
    *= temp;
}
cs


int형에 한에 정말 간단하고 편한 방법이지요.


main에서는 다음과 같이 사용할것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
 
    int a =10;
    int b =50;
 
    cout<<"*** Swap 함수 실행 전 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
    Swap(&a,&b);
    cout<<"*** Swap 함수 실행 후 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
 
    return 0;
}
cs


실행 결과는 다음과 같겠죠??


하지만 여기서 Swap을 숨겨저 포인터 인자인지를 모른다면..??


한번쯤은 이렇게 쓰고 싶어질것입니다.


1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
 
    int a =10;
    int b =50;
 
    cout<<"*** Swap 함수 실행 전 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
    Swap(a,b);
    cout<<"*** Swap 함수 실행 후 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
 
    return 0;
}
cs


그럼 Swap 함수는 이렇게 고쳐야 할꺼애요.


1
2
3
4
5
6
void Swap(int &a, int &b)
{
    int temp = b;
    b = a;
    a = temp;
}
cs


어느게 더 좋아 보이나요??


뭐.. 사용자에 다를테니.. 자세한 의견댓글로 남겨주시면 감사하겠습니다..


하지만 위의 방식은 int자료형에 한정되어있다는점이 아쉽죠.


char는? float는?


그럼 그때마다 함수를 계속 새로 만들어야 할까요?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void Swap(int &a, int &b)
{
    int temp = b;
    b = a;
    a = temp;
}
 
void Swap(float &a, float &b)
{
    float temp = b;
    b = a;
    a = temp;
}
 
void Swap(char &a, char &b)
{
    char temp = b;
    b = a;
    a = temp;
}

cs


이렇게 말이죠..? 너무 비효율적이죠. Swap이란 이름을 가진 함수는 뭐든간에 2개의 자리 또는 서로 교체 한다는 뜻일텐데 말이죠.


그래서 Template를 이용합니다.


1
2
3
4
5
6
7
template<typename T>
void Swap(T &a, T &b)
{
    T temp = b;
    b = a;
    a = temp;
}

cs


이러면 어떤가요? 이렇게 하면 어떤 자료형이라도 Swap이 가능합니다.


밑은 풀소스입니다.


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>
 
using namespace std;
 
template<typename T>
void Swap(T &a, T &b)
{
    T temp = b;
    b = a;
    a = temp;
}
 
 
int main()
{
 
    int a =10;
    int b =50;
 
    cout<<"*** Swap 함수 실행 전 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
    Swap(a,b);
    cout<<"*** Swap 함수 실행 후 2개의 값 : "<<a<<" , "<<b<<" ***"<<endl;
 
    return 0;
}
cs


감사합니다.


Posted by 시리시안

댓글을 달아 주세요

2016. 11. 30. 11:57

안녕하세요. 게임개발자 놀이터 입니다.

오늘은 C++에서 자주 쓰이지 않는! (개인적인 기준입니다..! 전 지금까지 써본적이 없었어요) friend에 대해서 정리 해볼까 합니다.


먼저 friend의 뜻을 생각해볼까요? 제목에도 적어놨지만 [친구]라는 뜻을 가지고 있습니다. C++에서 friend를 선언하면 친구 관계가 되는 거랍니다.  클래스끼리 말이죠. 그것도 굉장히 [친한 친구]! 베스트프렌드! 라고 생각하시면 이해가 좀 쉬울꺼애요.


모두가 그런건 아니겠지만, 여기선 이렇게 생각해봅시다. 

'나는 내 친한친구에게 나의 모든걸 보여줘도 괜찮아.'

'내 친구에게는 숨기는게 없어'

라는 뜻으로 friend를 생각하면 됩니다.


즉, A클래스가 B클래스를 friend로 지정한다면 B클래스는 A클래스의 private 멤버나 함수까지 접근이 가능합니다.

여기서 B클래스도 A클래스를 friend로 지정한다면 서로간의 private멤버나 함수까지 접근이 가능합니다.

(물론 B클래스는 A클래스를 friend로 지정 안할수도있습니다. (일방적인 사랑) )


선언 방법은 클래스 내부에 friend 로 선언해주면 되는데 이때 위치는 private, protected, public 어디든 상관 없습니다.


1
2
3
4
5
6
7
8
9
10
11
12
class A
{
    // 어디든 상관 없다! 
private:
    friend B;
 
protected:
    friend B;
 
public:
    friend B;
}
cs


위 코드처럼 firend는 어디에 선언되든 상관없습니다. ( 위 코드에선 A클래스가 선언되기전 B클래스를 알고있다고 생각합시다.)


그럼 직접적인 사용 예를 한번 보겠습니다.


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
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
 
using namespace std;
 
class B;
 
class A
{
 
private:
    int value;
public:
    A(int data)
    {
        value = data;
    }
    friend B;
};
 
class B
{
public:
    void fb(A a){
        cout<<"*** B클래스 public 함수 ***"<<endl;
        cout<<"*** A클래스 priavte int value에 접근 : "<<a.value<<" ***"<<endl;
    }
};
 
int main()
{
    A a(10);
 
    B b;
 
    b.fb(a);
 
    return 0;
}
cs


위 코드에서 25번째줄을 봅시다.

인자로 받아온 클래스 A의 value값에 접근하고있습니다.

value값은 private라 일반적으로는 절대 접근 할 수 없습니다.


하지만 friend가 이를 가능하게 만들어 줍니다.


실행 결과는 다음과 같습니다.


즉 friend로 선언 받은 이상 A클래스의 모든걸 접근할 수 있다는 것입니다.


이처럼 friend는 언뜻보면 아무런 이상 없고 정말 편해 보이지만, 이 friend는 객체지향의 핵심중 하나인 '정보 은닉'을 깨부수는 행위를 일으키게 됩니다.


어떤 클래스라도 private에 넣어둔 이유가 분명이 있을텐데, 이를 무시하고 접근해서 원한다면, 수정을 해버릴수도 있죠.


그래서 전 friend를 써본적이 없습니다..


이상으로 friend관련 글을 이만 마칩니다.


감사합니다.




Posted by 시리시안

댓글을 달아 주세요