CodeFights - alternatingSums

2018. 1. 15. 14:21

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

CodeFights - commonCharacterCount

2017. 12. 27. 23:12

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.


DLL_Injector.cpp

NotepadHook.cpp

 전에 올렸던것과 같지만 다시 후킹을 공부할겸 복습용으로 코드를 좀더 안정적으로 바꾸었다. 코딩 스타일을 통일하고 에러가 날만한 부분에 빠짐없이 리턴값을 확인하여 로깅한 것 뿐이지만 확실히 더 심리적으로 안정감이 느껴진다. 실무에서 로깅을 제대로 안해서 버그 잡기 힘들었던 것을 떠올리면 로깅은 제대로 해야한다는 생각이 들더라.

 두 번째 인자로 들어가는 DLL 파일 경로는 notepad.exe와 DLL파일이 같은 경로 내에 있거나 DLL파일이 환경변수 'PATH'에 등록되어있지 않다면 절대 경로를 써 주어야 정상 작동한다. 이유는 후킹 방식이 DLL 인젝션으로 notepad.exe 프로세스에 Thread를 만들어 돌리는 것이라 실행 경로도 notepad.exe 파일이 놓인 곳일 것이기 때문이라고 추론하고 있다. 혹시나 다른 이유라면 댓글로 알려주시길 바란다.


## 2019-06-17 추가 : 이 포스트의 소스코드는 WriteFile함수가 KernelBase.dll로 구현이 옮겨진 후의 환경에서만 동작합니다. (참고 : https://sanseolab.tistory.com/17)


DLL_Injector.cpp

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>
#include <Shlwapi.h>
 
DWORD findPID(LPCTSTR szProcessName);
BOOL injectDLL(DWORD dwPID, LPCTSTR szDLLName);
 
int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("[*] Usage : %s [Target] [DLL]\n", argv[0]);
        return 1;
    }
 
    if (!PathFileExists(argv[2])) {
        printf("[-] DLL Not Exists : %s\n", argv[2]);
        return 1;
    }
 
    DWORD pid = findPID(argv[1]);
    if (pid == 0xFFFFFFFF) {
        printf("[-] Process not found\n");
        return 1;
    }
    else {
        printf("[*] pid : %u\n", pid);
    }
    if (!injectDLL(pid, argv[2])) {
        printf("[-] Injection Failed\n");
        return 1;
    }
    else {
        printf("[*] Injection Successed\n");
    }
 
    return 0;
}
 
DWORD findPID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapshot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;
 
    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("[*] CreateToolhelp32Snapshot Error\n");
        return 0xFFFFFFFF;
    }
 
    Process32First(hSnapshot, &pe);
    do {
        if (!_stricmp(szProcessName, pe.szExeFile)) {
            dwPID = pe.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapshot, &pe));
 
    CloseHandle(hSnapshot);
    return dwPID;
}
 
BOOL injectDLL(DWORD dwPID, LPCTSTR szDLLName)
{
    HANDLE hProcess, hThread;
    HMODULE hMod;
 
    LPVOID pRemoteBuf;
    DWORD dwBufSize = lstrlen(szDLLName) + 1;
    LPTHREAD_START_ROUTINE pThreadProc;
 
    // Get target process handle
    if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) == INVALID_HANDLE_VALUE) {
        printf("[-] OpenProcess Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
 
    // Allocate memory to target process
    if ((pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE)) == INVALID_HANDLE_VALUE) {
        printf("[-] VirtualAllocEx Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
    
    // Write DLL name to target process memory
    if (WriteProcessMemory(hProcess, pRemoteBuf, szDLLName, dwBufSize, NULL== FALSE) {
        printf("[-] WriteProcessMemory Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
    
    // Get handle of "kernel32.dll"
    if ((hMod = GetModuleHandle("kernel32.dll")) == INVALID_HANDLE_VALUE) {
        printf("[-] GetModuleHandle Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
 
    // Get address of "LoadLibraryA"
    if ((pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA")) == INVALID_HANDLE_VALUE) {
        printf("[-] GetProcAddress Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
 
    // Create and run remote thread in target process
    if ((hThread = CreateRemoteThread(hProcess, NULL0, pThreadProc, pRemoteBuf, 0NULL)) == INVALID_HANDLE_VALUE) {
        printf("[-] CreateRemoteThread Error\n");
        printf("[-] gle : 0x%x\n", GetLastError());
        return FALSE;
    }
 
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
}
cs


NotepadHook.cpp

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>
#include <Windows.h>
 
#pragma pack(1)
struct IAT_STRUCT
{
    SHORT Opcode;
    LPVOID lpTarget;
};
 
typedef BOOL WINAPI tWriteFile(
    _In_        HANDLE       hFile,
    _In_        LPCVOID      lpBuffer,
    _In_        DWORD        nNumberOfBytesToWrite,
    _Out_opt_   LPDWORD      lpNumberOfBytesWritten,
    _Inout_opt_ LPOVERLAPPED lpOverlapped
);
 
tWriteFile* prevFunction;
tWriteFile* newFunction;
 
BOOL WINAPI NewWriteFile(
    _In_        HANDLE       hFile,
    _In_        LPCVOID      lpBuffer,
    _In_        DWORD        nNumberOfBytesToWrite,
    _Out_opt_   LPDWORD      lpNumberOfBytesWritten,
    _Inout_opt_ LPOVERLAPPED lpOverlapped
)
{
    if (nNumberOfBytesToWrite > 0)
        MessageBoxA(NULL, (LPCSTR)lpBuffer, NULLNULL);
    return prevFunction(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}
 
DWORD WINAPI tryHook()
{
    // Get address of target function
    LPVOID lpOrgFunc = NULL;
    if ((lpOrgFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile")) == NULL)
        return -1;
 
    // Backup old protect
    DWORD dwOldProtect;
    if (VirtualProtect(lpOrgFunc, 6, PAGE_EXECUTE_READWRITE, &dwOldProtect) == NULL)
        return -1;
 
    // Backup old function IAT
    IAT_STRUCT*  lpSavedFunc = (IAT_STRUCT*)VirtualAlloc(NULLsizeof(IAT_STRUCT), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    IAT_STRUCT   newFuncObj;
    memcpy_s(lpSavedFunc, 6, lpOrgFunc, 6);
    prevFunction = (tWriteFile*)lpSavedFunc;
 
    // Absolute Jump
    newFuncObj.Opcode = 0x25FF;
 
    // Set new functon to replace
    newFunction = &NewWriteFile;
    newFuncObj.lpTarget = &newFunction;
 
    // Replacing
    memcpy_s(lpOrgFunc, 6&newFuncObj, 6);
 
    // Rollback protection
    VirtualProtect(lpOrgFunc, 6, dwOldProtect, NULL);
    return 0;
}
 
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    HANDLE hThread;
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        tryHook();
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
cs


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

리눅스 커널 모듈 프로그래밍 - mychardev


이전 글에서 올렸던 이 문서 이후 예제를 이용하여 아주 간단한 Character Device Driver를 작성했다. 사실상 거의 코드를 가져다 썼지만 내용 이해를 위해 printk를 몇번 하여 좀더 보기 편해졌다.




결과 화면은 다음과 같다.



블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

리눅스 커널 모듈 프로그래밍 - Hello World


 리눅스 커널에는 모듈의 개념이 있어 커널을 통째로 컴파일하고 Reboot하지 않고도 커널의 기능을 확장할 수 있다. 리눅스 커널에 대해 책만 보고 공부하다가 도저히 안 될것 같아 일단 간단하게나마 리눅스 커널 모듈을 만들고 마운트하는 실습을 해 보았다. 실습에 사용한 소스와 내용 모두 이 문서를 이용하였다.


 예제는 프로그래밍을 처음 배우는 사람이라면 누구나 한번쯤은 해보는 "Hello World" 출력이다. 단, 커널은 로드와 언로드 두가지 이벤트에서 출력이 가능하므로 "Goodbye World"도 출력하도록 하겠다.

 소스코드는 다음과 같이 매우 간단하다.


1
2
3
4
5
6
7
8
9
10
11
12
#include <linux/module.h>
#include <linux/kernel.h>
 
int init_module() {
    printk(KERN_INFO "Hello world!\n");
    return 0;
}
 
 
void cleanup_module() {
    printk(KERN_INFO "Goodbye world!\n");
}
cs


 커널 모듈은 적어도 모듈이 커널에 로딩될 때 호출되는 init_module()과 모듈이 커널에서 언로딩될 때 호출되는 cleanup_module() 두 가지 함수를 가져야 한다. 커널 버전 2.3.13 이후로는 함수명에 관계없이 시작/종료함수를 지정할 수 있지만 여기서는 편의상 그냥 사용한다.


 C언어를 배웠다면 출력함수로 printf가 익숙하겠지만 커널 모듈에서는 printk를 사용한다. printk 함수에는 우선순위로 8가지의 속성을 입력할 수 있는데, 이는 <linux/kernel.h> 내부에 정의되어 있으므로 가져다 쓰면 된다. 참고로 8가지의 속성은 다음과 같다.


33
34
35
36
37
38
39
40
#define    KERN_EMERG    "<0>"    /* system is unusable            */
#define    KERN_ALERT    "<1>"    /* action must be taken immediately    */
#define    KERN_CRIT    "<2>"    /* critical conditions            */
#define    KERN_ERR    "<3>"    /* error conditions            */
#define    KERN_WARNING    "<4>"    /* warning conditions            */
#define    KERN_NOTICE    "<5>"    /* normal but significant condition    */
#define    KERN_INFO    "<6>"    /* informational            */
#define    KERN_DEBUG    "<7>"    /* debug-level messages            */
cs


 커널 모듈을 컴파일할때는 make 명령과 Makefile을 이용한다. Makefile 문법을 여기 적긴 뭐하고 다음 코드를 Makefile 이라는 이름의 파일에 작성하고 저장하면 된다. Makefile은 make로 컴파일을 진행하기 위해 사용하는 명세라고 보면 편하다. all과 clean 부분은 사실 필요없지만 편의를 위해 작성했다고 한다.


 전부 작성했으면 해당 디렉토리 내에서 make 명령을 실행해 보자. 결과가 다음과 같이 나온다면 성공이다.



 이제 디렉토리에는 HelloWorld.ko 파일이 만들어졌을 것이다. 이것이 커널 모듈 파일이며, 이는 insmod 명령을 이용해 커널에 로드할 수 있다. 또한 rmmod 명령을 이용해 커널에서 제거도 가능하다.


1
2
insmod HelloWorld.ko
rmmod HelloWorld
cs


위의 두 명령을 수행해 보고, /var/log/kern.log 파일을 열어서 확인해 보면 다음과 같이 가장 밑에 코드에 적어둔 "Hello World"와 "Goodbye World"가 적혀 있는 것을 볼 수 있을것이다.



 이걸로 간단히 커널 모듈을 컴파일하고 로드/언로드하는 실습을 해보았다.

나중에 공부하면서 다른 모듈도 좀 더 작성해봐야겠다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

(기록용) Python 2.7 사용중


flask : REST API

requests : HTTP request & response

pymysql : MySQL 커넥션 및 쿼리 (SQLAlchemy는 취향이 아님)

psutil : 프로세스 관리

shutil : 파일 및 디렉토리 관리

celery : redis와 연동하여 사용하는 비동기 작업 큐 (http://dgkim5360.tistory.com/29)

gunicorn : WSGI

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


 좀 큰 프로그램을 짜다 보면, 특히 한번 돌고 말 것이 아닌 지속적으로 돌아야 하는 프로그램을 짜다 보면 로깅의 필요성을 아주 절실하게 느끼게 된다. 버그가 발생했다면 언제 버그가 발생했고, 어떤 버그가 어디에서 무엇이 원인으로 발생했는지를 잘 기록해 두어야 프로그램을 유지보수하기 편해진다.


 파이썬에는 로깅을 쉽고 빠르고 간편하게 처리할 수 있게 해주는 내장 모듈이 존재한다.

logging 이라는 모듈인데, 이를 사용하면 로그 포맷팅이나 크기 관리 등이 굉장히 편해진다.


https://docs.python.org/2/library/logging.html

파이썬 2에서의 logging 문서이다.

기초적인 사용법에 대해서는 여기에 적지 않겠다.



 logging 모듈을 사용하다 보면 로깅 자체는 간단하게 할 수 있어 좋지만 logger를 만드는 데에 핸들러도 만들어야 하고, 핸들러마다 포맷터도 필요하고, 포맷터를 핸들러에 등록하고 핸들러를 다시 logger에 등록하는 등 상당히 귀찮은 과정을 지나야 한다는 것을 느낄 수 있다. 그래서 보통 다음과 같은 CreateLogger 함수를 만들어서 사용한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import logging
import logging.handlers
 
def CreateLogger(logger_name):
    # Create Logger
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
 
    formatter = logging.Formatter('\n[%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
    
    # Create Handlers
    streamHandler = logging.StreamHandler()
    streamHandler.setLevel(logging.DEBUG)
    streamHandler.setFormatter(formatter)
 
    logger.addHandler(streamHandler)
    return logger
cs


 위와 같이 logger를 만들어주는 함수를 짜서 Utils.py 같은 곳에서 호출하여 사용하면 상당히 편리해진다.

그런데 위 코드에는 문제가 하나 있다.


다음과 같은 코드를 보면,


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
import logging
import logging.handlersd
 
def CreateLogger(logger_name):
    # Create Logger
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
 
    formatter = logging.Formatter('\n[%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
    
    # Create Handlers
    streamHandler = logging.StreamHandler()
    streamHandler.setLevel(logging.DEBUG)
    streamHandler.setFormatter(formatter)
 
    logger.addHandler(streamHandler)
    return logger
 
def Func1():
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Func1!")
 
def Func2():
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Func2!")
 
if __name__ == '__main__':
    Func1()
    Func2()
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Main!")
cs


기대하는 결과값은 "Hello, Func1!"과 "Hello, Func2!"와 "Hello, Main!"이 순서대로 하나씩 나오는 것이다.

하지만 이를 실행했을 때 나오는 결과값은 다음과 같다.


눈치가 빠른 사람들은 벌써 이게 왜 잘못되었는지 알아챘을 것이다.


 이런 문제가 생기는 것은 logging 모듈에서 getLogger()를 통해 새로운 logger를 받아올 때, 같은 logger 이름일 경우 새로 logger를 만들지 않고 이미 만들어진 logger를 그대로 리턴하는 싱글턴 패턴을 사용하고 있기 때문이다.


 간단하게 말해서 Func1에서 "MyLogger"라는 이름의 logger를 하나 만들었을 때와 Func2에서 "MyLogger"라는 이름의 logger를 하나 만들었을 때, 두 logger는 같은 것이 된다는 것이다. 같은 logger에 핸들러를 계속해서 붙이므로 한번 logging해도 여러 개의 핸들러가 작동하여 로그는 여러 개가 찍히는 것이다.


실제로 이를 테스트해 보면 다음과 같은 결과가 나온다.


이런 문제를 해결하기 위해서는 어떻게 해야 할까?

이를 해결하기 위해서는 싱글턴 패턴의 특성을 이용하는 것이 좋다.


getLogger()를 이용하여 가져온 logger가 이미 핸들러를 가지고 있다면, 이미 핸들러가 등록되어 있으므로 새로운 핸들러를 또 등록할 이유가 없다. 따라서 위의 잘못된 소스코드를 다음과 같이 수정하면 제대로 된 결과를 얻을 수 있다.


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
import logging
import logging.handlers
 
def CreateLogger(logger_name):
    # Create Logger
    logger = logging.getLogger(logger_name)
 
    # Check handler exists
    if len(logger.handlers) > 0:
        return logger # Logger already exists
 
    logger.setLevel(logging.DEBUG)
 
    formatter = logging.Formatter('\n[%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
    
    # Create Handlers
    streamHandler = logging.StreamHandler()
    streamHandler.setLevel(logging.DEBUG)
    streamHandler.setFormatter(formatter)
 
    logger.addHandler(streamHandler)
    return logger
 
logger = CreateLogger("Utils")
 
def Func1():
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Func1!")
 
def Func2():
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Func2!")
 
if __name__ == '__main__':
    Func1()
    Func2()
    logger = CreateLogger("MyLogger")
    logger.info("Hello, Main!")
cs



블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,



 C++ 통신 공부를 위해 Boost::Asio를 사용해 봤다. 아무 생각 없이 예제 보며 따라만들다가 클래스화해볼까 하고 멋대로 아무 설계도 없이 클래스화하고 했더니 난리가 났다 (._.


다음부터 짤땐 제대로 설계하고 만들어야겠다 ㅠㅠ



1. EchoClient


1-1. EchoClient.h


1-2. EchoClient.cpp



1-3. Main.cpp



2. EchoServer


2-1. EchoServer.h


2-2. EchoServer.cpp


2-3. Main.cpp


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


 Boost는 전 세계적으로 유명한 C++ 오픈소스 라이브러리이다.

Boost의 가장 큰 특징이자 장점은 내부적으로 수많은 플랫폼들의 표준을 이용하기 때문에 멀티플랫폼을 지원한다는 것이다. 이 Boost를 사용하기 위해 Windows에서 Visual Studio로 빌드하는 과정을 적어보았다.



첫째로, 우선 Boost를 다운받고, 압축을 풀어야 한다.


http://www.boost.org/




 위 사이트에 접속하면 우측에 GET BOOST 라고 적힌 붉은 버튼 이미지가 있다.

거기서 최신 버전을 다운받을 수 있으니 이를 다운받고, 원하는 위치에 압축을 풀어준다.

2016년 12월 20일 기준으로 최신 Release 버전은 1.62.0 버전이다.


 압축을 풀고 boost_1_62_0/bootstrap.bat 파일을 실행시킨다.

커맨드라인 창이 하나 뜨는데, 작업이 완료될 때까지 기다려 줘야 한다.

몇 분이면 끝난다.


 작업이 완료되고 나면 같은 디렉토리에 b2.exe 파일과 bjam.exe 파일이 생성된다.

먼저 b2.exe를 실행하고 끝난 후에 bjam.exe를 실행하면 되는데 b2.exe가 상당히 시간이 오래 걸린다.

인내심을 가지고 기다리자. 작업이 끝나고 나면 같은 디렉토리에 stage/lib 디렉토리가 생성되었을 것이다.


이 둘까지 마치고 나면 사용할 준비는 다 된 것이다.




 Visual Studio에서 프로젝트를 하나 만들고, Properties -> C/C++ -> General -> Additional Include Directories에 boost_1_62_0를 지정해 준다.

그 안에 든 'boost'가 아니라 'boost_1_62_0'인 이유는 일부 소스 파일들이 'boost'를 루트 디렉토리로 요구하기 때문이다.




 다음으로 똑같이 Properties -> Linker -> General -> Additional Library Directories 에 boost_1_62_0\stage\lib 을 넣어주자.

이후 빌드하면 정상적으로 빌드되며 실행도 잘 된다.




블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


 unique_ptr은 이름에서부터 알 수 있듯이 한 인스턴스에 대한 소유권을 단 하나의 포인터에만 허락하는 스마트 포인터이다. 따라서 포인터간의 복사는 불가능하며, 이동은 가능하지만 std::move라는 함수를 이용해야만 가능하다.


저번에 사용한 것과 같이 다음과 같은 클래스가 있다고 가정해 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass
{
public:
    int num;
    MyClass()
    {
        std::cout << "Ctor" << std::endl;
    }
 
    ~MyClass()
    {
        std::cout << "Dtor" << std::endl;
    }
};
cs


 int형 변수와 생성자, 소멸자에 출력문을 하나씩 갖고 있는 심플한 클래스이다.

이를 다음과 같이 unique_ptr을 이용해 main에서 생성, 출력을 하면 알아서 메모리를 해제해 준다.


1
2
3
4
5
6
7
8
int _tmain(int argc, _TCHAR* argv[])
{
    std::unique_ptr<MyClass> myClass(new MyClass);
    myClass->num = 1234;
    std::cout << myClass->num << std::endl;
    return 0;
}
 
cs




unique_ptr을 복사하려고 하면 컴파일 시점에 이렇게 에러가 발생한다.


error C2280: 'std::unique_ptr<MyClass,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': 삭제된 함수를 참조하려고 합니다.


한 인스턴스를 두개 이상의 포인터가 가리키는것은 불가능하므로 이동만 가능한데, 다음과 같은 방식으로 이동이 가능하다.


1
2
3
4
5
6
7
8
9
int _tmain(int argc, _TCHAR* argv[])
{
    std::unique_ptr<MyClass> myClass(new MyClass);
    std::unique_ptr<MyClass> myClass2 = std::move(myClass);
    myClass2->num = 1234;
    std::cout << myClass2->num << std::endl;
    return 0;
}
 
cs


std::move 이후 myClass는 NULL로 초기화되며 myClass2에 해당 인스턴스의 주소가 할당된다.




 weak_ptr은 shared_ptr의 단점을 보완하는 데에 사용되는 포인터이다.

shared_ptr의 경우 순환 참조(Circular references)라는 문제가 발생할 수 있다.

이는 shared_ptr가 서로를 참조하여 둘 모두 반환이 되지 않는 상태로, 일종의 메모리 데드락이라고 볼 수 있다.


 shared_ptr은 내부적으로 레퍼런스 카운팅을 통해 해당 인스턴스를 가리키는 포인터의 개수를 세지만, 사실 이 레퍼런스 카운트 변수는 두 가지의 종류가 있다. 이중 strong_count는 shared_ptr에서 메모리의 해제에 직접적으로 관여하는 카운트로, 0이 되면 메모리가 자동으로 해제된다. weak_count는 간단하게 같은 인스턴스를 가리키는 weak_ptr의 개수로, 이는 메모리의 해제에 영향을 미치지 않는다.


 weak_ptr은 기본적으로 shared_ptr과 같은 주소를 가리키지만 객체에 직접적인 참조는 불가능하다. 참조를 위해서는 lock() 함수를 이용해 shared_ptr을 반환받은 후 사용해야 한다. 만약 weak_ptr이 남아 있는 상태에서 shared_ptr이 전부 사라지면 메모리는 해제되고 lock() 함수에서는 nullptr이 반환된다. 그 반대로 weak_ptr이 전부 사라진다고 해도 shared_ptr이 남아 있다면 메모리는 해제되지 않는다.

shared_ptr의 참조자와 비슷한 역할을 하는 것이다.


http://blog.naver.com/wlstn3273/220713667682


 좋은 예시가 여기에 있으므로 여길 보고 따라가 보면 개념이 제대로 잡힐 것이라고 생각한다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,