1. memset( array, 0, sizeof array );

전달된 배열을 0으로 초기화 한다.

ex)

1
2
3
4
char outputArray[30];
 
//array init by 0
memset( outputArray, 0sizeof(outputArray) );
cs

2. std::copy( input_begin, input_end, output_begin );
어떠한 컨테이너도 복사한다. 
output에 해당하는 컨테이너의 크기가 input의 컨테이너보다 크거나 같아야 한다.
ex)
1
2
3
4
5
6
7
8
9
10
11
12
char outputArray[30];
std::string inputArray = "A B C D E F G H I J K L"  //size = 23
 
//array init by 0
memset( outputArray, 0sizeof(outputArray) );
 
std::copy(std::begin(inputArray), std::end(inputArray), std::begin(outputArray));
 
//show
/** 
 * outputArray = "A B C D E F G H I J K L\0\0\0\0\0\0\0"
 **/
cs



3. std::advance( iterator, index );
어떠한 컨테이너도 배열처럼 접근한다.
iterator를 index만큼 증가시킨다. 
index는 0보다 크거나 같고 컨테이너의 사이즈보다 작아야 한다.
ex)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::map<std::stringstd::string> nameMap;
// init map 
nameMap.emplace(std::pair<std::stringstd::string>("Hong" , "Hello Hong" ));
nameMap.emplace(std::pair<std::stringstd::string>("Seong""Hello Seong"));
nameMap.emplace(std::pair<std::stringstd::string>("Hee"  , "Hello Hee"  ));
 
auto item = nameMap.begin();
std::advance( item, random<int>(0, nameMap.size()-1) );
 
//show
/** 
 * randomIndex = 0, data = "Hello Hong"
 * randomIndex = 1, data = "Hello Seong"
 * randomIndex = 2, data = "Hello Hee"
 **/
cs


4. std::find( array_begin, array_end, item );
컨테이너 중 item의 iterator를 반환한다.
5. std::distance( array_begin, iterator );
컨테이너에서 iterator에 해당하는 index를 반환한다.
ex)
1
2
3
4
5
6
7
auto iter = std::find(list.begin(), list.end(), item);
int index = std::distance(list.begin(), iter);
 
//show
/**
 * list[index] 
 **/
cs


6. std::fill( array_begin, array_end, value );
컨테이너를 value로 채운다. (초기화 한다.)

ex)

1
2
3
4
5
6
7
8
9
10
std::vector<int> array;    
array.resize(3);
std::fill(array.begin(), array.end(), 10);
 
//show
/**
 * array[0] = 10
 * array[1] = 10
 * array[2] = 10  
 */
cs


'All > C++' 카테고리의 다른 글

operator new / operator delete  (0) 2016.03.14
메모리 누수 검사  (0) 2015.12.29
가변인자 로그 출력함수  (0) 2015.12.29
RValue와 LValue  (2) 2015.12.28
문장줄이기  (0) 2015.12.28

new와 malloc의 차이점 포스트가 아니니 아래의 내용은 안읽고 바로 operator new에 대해서 읽어도 무방하다.

operator new와 malloc의 차이점은 아래와 같다.

1. new는 생성자를 호출하지만 

 - malloc은 생성자를 호출하지 않는다.

2. new는 호출한 클래스 타입의 포인터를 반환하지만 

- malloc은 void*로 반환한다.

3. new로 동적할당을 하면 delete로 해제해 주어야 하지만

- malloc은 free로 해제 해야한다.

4. 추가로 delete는 소멸자를 호출하고 

- free는 호출하지 않는다.


자, 그럼 이제 왜 operator new를 설명하는데 위 개념이 필요한지 알아보자.


operator new

new가 하는일은 위에서도 설명했듯이 

1. 메모리를 할당하고 

2. 생성자를 호출하여 초기화 해준다.

위 두 가지 일은 사용자가 아무리 바꾸려해도 바뀌지 않는 불변의 법칙이다. 

사용자가 바꿀 수 있는 것이라고는 new에서 수행하는 첫번째 일인 '메모리를 할당하는 방법'이다. (두번째는 이후에 설명하겠다.) 

다시말해 new함수에서 메모리를 할당하기위해 malloc과 같은 기능의 어떤 함수를 호출하는데 

 어떤 함수를 오버로딩하여 변화를 줄수 있다는 의미이다.

그 함수의 이름이 operator new인 것이다. 

operator new를 오버로딩하게 되면 new에서 호출하는 operator new가 바로 내가 오버로딩한 함수가 되는 것이다. 



얼마나 할일이 없으면 저런것까지 오버로딩을 하지? 라는 생각을 하고 있다면 큰 오산이다.

위에서 말했듯이 메모리를 할당하는 방법을 오버로딩한다는 것은 다음과 같은 것을 할 수 있다는 이야기이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CObject* CObject::create()
{
    // 여기서 operator new가 호출됨
    CObject* pRet = (CObject*)new(std::nothrow)CObject();
    if (pRet && pRet->init())
    {
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}
 
void* CObject::operator new(size_t sizeconst std::nothrow_t)
{
    // PoolingManager에서 메모리를 할당 받는다.
    return CPoolingManager::Instance()->ObjectNew();
}
cs

어떤 Object클래스의 한 부분이다. 

이 클래스는 생성자와 소멸자를 private으로 은닉화 시켜놓고 create으로만 객체를 생성할 수 있다. 

(이렇게 하면 생성시 꼭 해야할 일들을 수행할 수 있다.  )

create함수를 보면 new를 하고 있는데 이때 new안에서 호출되는 operator new함수는 내가 오버로딩한 것이다. 

때문에 실제 메모리는 할당되지 않으며 내가 오버로딩한 함수를 호출하는데, 

내가 오버로딩해 놓은 함수를 보니 어딘가에서 메모리를 얻어오는 것을 알 수 있다.(이부분에 대해서는 [MemoryPooling_2] 포스트를 참고하자)

위와 같은 방식으로 메모리를 조금더 효율적으로 할당할 수 있게 된다.(마찬가지로 무엇이 효율적인지에 대해서는 [MemoryPooling_1] 포스트를 참고하자)


operator delete

기본적인 원리는 앞에서 설명한 것과 같다. 단지 메모리를 해제하는 방식을 operator delete로 오버로딩하여 변경할 수 있다는 것이 다를 뿐이다.

1
2
3
4
5
void* CObject::operator delete(void* p)
{
    // PoolingManager에서 메모리를 메모리 블럭으로 되돌린다.
    return CPoolingManager::Instance()->ObjectDelete(p);
}
cs

PoolingManager의 ObjectDelete를 호출하는데 이 함수안에서는 메모리를 해제하는 것이 아니라 Free상태로 놓는다.

(Free상태의 메모리는 다시 create함수를 이용하여 사용상태로 전환될 수 있는 휴면상태 메모리이다.)


생성자 호출

위와 같은 방식으로 메모리를 초기화 하면 말 그대로 순수 메모리를 얻게될 것이다. 

마치 malloc으로 얻은 메모리처럼 말이다.

위에서는 마치 일반적인 operator new가 메모리도 할당하고 생성자도 알아서(?) 호출해주는 것처럼 말했지만 

사실 메모리 할당과 생성자 호출은 별개의 문제이다.

일반적으로 operator new를 호출 한다고 해서 생성자까지 호출해주지는 않는다.

위의 방식처럼 사용하기 위해서는 생성자가 호출되어야 하는데 말이다. (메모리 블럭을 객체로사용하는 방식) 

이것을 가능하게 해주는 것이 바로 위치지정(placement) operator new라는 것이다. 

쉽게 operator new의 확장판이라 인자로 전달되는 포인터 타입의 생성자를 호출해준다고 이해하면되겠다.


이렇게 생성자 호출이 끝나야만 해당 메모리는 제대로 객체로써 이용할 수 있게된다.

'All > C++' 카테고리의 다른 글

자주 사용하는 STL function  (0) 2016.11.28
메모리 누수 검사  (0) 2015.12.29
가변인자 로그 출력함수  (0) 2015.12.29
RValue와 LValue  (2) 2015.12.28
문장줄이기  (0) 2015.12.28

메모리 누수를 검사하는 방법에 대한 포스팅

프로그래밍을 하다보면 메모리를 할당하고 해제하는 것을 잊어 버리는 경우가 생기기 마련이다. 

1
2
3
4
5
6
7
8
#include <iostream>
 
int main()
{
    int * pA = new int(10);    // 메모리 할당
    std::cout << *pA << std::endl// 사용
    //delete pA; // 메모리 해제
}
cs

위의 코드는 메모리릭이 나고 있다. 작은 프로그램의 경우 찾기 쉽겠지만 조금이라도 규모가 있다면 찾기 매우 어렵다. 

이때 해결하는 여러방법이 있겠지만 그 중 어떤 메모리가 어디서누수 되었는지 알 수 있는 방법이 있다.
코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <crtdbg.h>
 
int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    //_CrtSetBreakAlloc(151); //여기에 넘버를 넣는다.
 
    int * pA = new int(10);
    std::cout << *pA << std::endl;
    //delete pA;
 
    _CrtDumpMemoryLeaks();
}
cs

위의 코드를 실행시키면 출력창에 아래와 같이 출력된다.

1
2
3
4
5
Detected memory leaks!
Dumping objects ->
{151} normal block at 0x005BCB784 bytes long.
 Data: <    > 0A 00 00 00 
Object dump complete.
cs


이때 151이라는 숫자를 _CrtSetBreakAlloc(151) 함수안에 대입하고 다시 실행하면 아래와 같이 실행 중에 해당 메모리가 할당되는 위치에 브레이크가 걸린다.


이로써 메모리 누수를 검사하는 방법을 알아보았다.

+  _CrtDumpMemoryLeaks(); 은 반드시 프로그램의 마지막 위치에서 호출해야한다.

'All > C++' 카테고리의 다른 글

자주 사용하는 STL function  (0) 2016.11.28
operator new / operator delete  (0) 2016.03.14
가변인자 로그 출력함수  (0) 2015.12.29
RValue와 LValue  (2) 2015.12.28
문장줄이기  (0) 2015.12.28

매번 새프로젝트를 생성할 때마다 절실하게 필요한 몇몇 함수가 있는데 그 중 하나가 로그 출력 함수인 것같다.

아래는 윈도우 기반의 가변인자 로그출력, 메시지 박스 출력, 스트링 합성 함수이다. 
유니코드와 멀티바이트 모두에서 사용 가능하도록 구현하였다.


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
#include <iostream>
#include <stdarg.h>
#include <tchar.h>
#include <Windows.h>
#define DEBUG_MODE 1
typedef std::basic_string<TCHAR> tstring;
 
tstring MakeString(const TCHAR* str, ...){
 
    va_list args;
    int len;
    // 시작.
    va_start(args, str);
 
    // 가변인자로 이루어진 문자열의 크기를 구한다.
    len = _vsctprintf(str, args) + 1// _vscprintf doesn't count terminating '\0'
 
    // 위에서 구한 크기+1만큼 buffer에 메모리를 할당한다.
    TCHAR* buffer = new TCHAR[len + 1];
 
    // 문자열을 buffer에 입력한다.
    _vstprintf(buffer,  str, args);
    
    // 끝.
    va_end(args);
 
    tstring s(buffer);
 
    delete[](buffer);
 
    return s;
}
 
void CLog(const TCHAR* str, ...)
{
#if(DEBUG_MODE >= 1)
    va_list args;
    int len;
    // 시작.
    va_start(args, str);
 
    // 가변인자로 이루어진 문자열의 크기를 구한다.
    len = _vsctprintf(str, args) + 1// _vscprintf doesn't count terminating '\0'
 
    // 위에서 구한 크기+1만큼 buffer에 메모리를 할당한다.
    TCHAR* buffer = new TCHAR[len];
 
    // 문자열을 buffer에 입력한다.
    _vstprintf(buffer, str, args);
 
    // 끝.
    va_end(args);
 
    OutputDebugString(buffer);
    OutputDebugString(_TEXT("\n"));
 
    delete[](buffer);
#endif
}
 
 
void CLogBox(TCHAR * szCaption, TCHAR * szFormat, ...)
{
    // 가변인자로부터 문자열 얻기.
    va_list args;
    int len;
    // 시작.
    va_start(args, szFormat);
 
    // 가변인자로 이루어진 문자열의 크기를 구한다.
    len = _vsctprintf(szFormat, args) + 1// _vscprintf doesn't count terminating '\0'
 
    // 위에서 구한 크기+1만큼 buffer에 메모리를 할당한다.
    TCHAR* buffer = new TCHAR[len + 1];
 
    // 문자열을 buffer에 입력한다.
    _vstprintf(buffer, szFormat, args);
 
    // 끝.
    va_end(args);
    MessageBox(NULL, buffer, szCaption, MB_OK);
 
    delete[](buffer);
}
 
void main()
{
    tstring msg = MakeString(_TEXT("%s"), _TEXT("안녕하세요."));
    CLog(_TEXT("%s"), msg.c_str());
    CLogBox(_TEXT("알림"), _TEXT("%s"), _TEXT("메시지 박스 출력"));
}
 
cs


'All > C++' 카테고리의 다른 글

자주 사용하는 STL function  (0) 2016.11.28
operator new / operator delete  (0) 2016.03.14
메모리 누수 검사  (0) 2015.12.29
RValue와 LValue  (2) 2015.12.28
문장줄이기  (0) 2015.12.28

LValue란 


LeftValue, 단일 표현식을 넘어가도 존재하는 값을 의미한다. (영속성)

예를들어 

int = 0; 

char* Name = "홍길동";

++i;

위에서 볼드체로 된 값들은 문장이 넘어가도 존재한다. 




RValue란 


RightValue, 단일 표현식이 넘어가면 없어지는 값을 의미한다. (비영속성)

예를들어

int a = 10

int b = 20;

char* Name = "홍길동";

​int c = a + b;

​c++;

위에서 붉은색 글씨로 된 값들은 그 당시에만 있는 임시값이다.


다른 것들은 쉽게 이해가 된다. 하지만 ++i와 c++는 쉽사리 이해 되질 않는다.

차근차근 알아보자 ++i의 경우에는 연산자오버로딩을 해보면 


1
2
3
4
5
6
7
//point 클래스의 단항연산자 오버로딩(전위)
Point& Point::operator++()
{
    ++m_fX;
    ++m_fY;
    return *this;
}
cs


이렇게 자기자신을 리턴한다. 이는 영속한 데이터이다. 하지만 c++의 경우 연산자오버로딩에서 보면


1
2
3
4
5
6
7
//point 클래스의 단항연산자 오버로딩(후위)
Point Point::operator++(int)
{
    Point temp = *this;
    ++(*this);
    return temp;
}
cs


이렇게 복사본을 리턴한다. 때문에 연산후에도 값이 그대로이며 다음식에서 ++되는 것을 알수있다.

또한 복사본이기 때문에 비영속적이다. 

이것이 ++i가 LValue이고 c++가 RValue인 이유라 할 수 있다.


위의 내용이 어렵다면 단순히 해당 식에서 주소를 알아낼 수 있다면 lvalue 불가능하다면 rvalue라 할수도 있다. 예를들어 

&i는 가능하지만 &(a + b)는 불가능하다.




서론이 길었는데 원래 포스트의 주제는 RValue를 참조하는 방법에 관한 것이었음으로 이제 본론으로 들어가겠다. 예를 들어


Print(const char& a) //알파벳 하나를 넘겨받아서 출력함 


이라는 함수가 있다. 잠시 설명을 하자면 a는 레퍼런스로 매개변수를 넘겨받는다.

레퍼런스이니 당연히 메모리가 할당되지 않으며 변수에 별명을 붙이는 형식이므로 마치 포인터처럼 사용이 가능하다.(값 복사 없이 참조가 가능하다는 뜻) 

또한 const로 선언되어 있기에 값을 변경할 수 없다.

즉 const든 non_const든 영속적인 LValue가 저장 가능하다.

결과적으로 a로 넘어오는 파라미터 값이 무엇이든 char형 변수일때 출력이 가능하다.

하지만 위의 함수는 print('a');와 같이 RValue를 파라미터로 호출할 수 없다.

(영속적인 데이터만 저장가능한 변수이기에 파라미터로 넘겨 받을 수 없다.)


때문에 RValue를 저장할 수 있는 레퍼런스가 필요하며 그것이 바로 && 이다.

print(const char&& a); 

이와 같이 정의하면 const든 non_const든 비영속적인 RValue가 저장가능하다.

때문에 print('a');와 같은 호출이가능하다.

'All > C++' 카테고리의 다른 글

자주 사용하는 STL function  (0) 2016.11.28
operator new / operator delete  (0) 2016.03.14
메모리 누수 검사  (0) 2015.12.29
가변인자 로그 출력함수  (0) 2015.12.29
문장줄이기  (0) 2015.12.28

오늘 포스팅할 내용은 로컬라이징에 관련된 내용이다.

프로젝트를 다른 언어로 번역하는 과정에서 가장 큰 이슈는 

아마 번역한 문장의 길이와 UI의 부적합이 아닐까 생각한다.

한글로 썼을때 짧은 문장이 다른 언어로 번역되면서 매우 긴 문장으로 바뀌는 경우가 많기 때문이다.

이럴때 어떤 나라의 언어던지 원하는 글자 수에서 끊고 뒤에 [...]을 이어 붙여주는 함수가 있다면 좋겠다는 생각에 구현해보았다.



먼저 함수의 역할은 다음과 같다.

- std::string CutStr(std::string* targetString, size_t length)

- 문장과 자르고 싶은 길이를 입력받고 출력은 잘린 문자열 뒤에 [...]이 붙어져 있어야 할 것이다.

ex 1) CutStr("ABCDEFGHIJ", 5)          =>  "ABCDE..."

ex 2) CutStr("동해물과백두산이", 5)      =>  "동해물과백..."

ex 3) CutStr("A동B해C물D과E백F", 5)    =>  "A동B해Z..."



사실 영어의 경우 문자의 길이가 곧 바이트 수이기 때문에 strlen함수로 길이를 알아내어 간단하게 끊을 수 있다.

하지만 한글이나 일본어 등은 3바이트이기 때문에 [가나다] = 9가 된다. 

또 영어나 한글을 같이 쓸경우 각 문자의 바이트수를 알아내어 계산해야 한다. 

구현한 결과는 아래와 같다.


코드


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
#include <iostream>
 
using namespace std;
 
string cutStr(string targetString, size_t limitNum)
{
    // 원래의 문장 저장
    string curString = targetString;
    
    // 제한길이까지의 바이트 수
    size_t byteNum = 0;
    char strArr[1024= {0,};
    
    // 각 문자가 어떤 언어인지 알 수 있게 char형으로 저장
    strcpy( strArr, curString.c_str() );
    
    // 각 배열에 저장되어 있는 값이 아스키 코드값이 존재하는 지에 따라 문자구분.
    for(size_t count = 0; count < limitNum ; count++)
    {
        // 아스키값이 없으면 3바이트로 처리
        if((unsigned char)strArr[byteNum] & 0x80)
        {
            byteNum += 3;
        }
        // 아스키값이 있으면 1바이트로 처리
        else
        {
            byteNum += 1;
        }
    }
    // 전체 바이트 수가 제한 길이까지의 바이트 수 보다 크면 문장을 잘라야 함
    if(curString.length() > byteNum)
    {
        // 제한길이까지의 바이트 수만큼 문장을 자른다.
        string cuttedString = targetString.substr(0, byteNum);
        // 뒤에 [...]을 붙여준다.
        cuttedString.append("...");
        return cuttedString;
    }
    // 자를 필요가 없으면 그냥 문장 리턴
    return curString;
}
 
int main()
{
    string string1 = "ABCDEFG";
    string string2 = "가나다라마바";
    string string3 = "A가B나C다D라";
    
    printf("%s\n", cutStr(string1, 5).c_str());
    printf("%s\n", cutStr(string2, 5).c_str());
    printf("%s\n", cutStr(string3, 5).c_str());
}
cs



결과

1
2
3
ABCDE...
가나다라마...
A가B나C...
cs



원리는 0x80 과 문자배열의 요소의 &연산(1000.0000 & 111.1111)으로 해당 문자가 1 ~ 127  범위안 (아스키 코드)인지 판단하여 범위 안이면 1바이트 밖이면 3바이트 로 계산하여 제한 길이까지의 바이트 수를 저장한다. 

그후 구한 바이트 수가 문자열의 총 바이트 수보다 작다면 문자열을 자른후 [...]을 붙인다.


아스키코드표의 문자들을 제외하면 전부 3바이트로 처리하니 정확한 것은 아니지만 UTF-8인코딩 에서 3바이트로 처리되는 언어들에서는 충분히 사용가능할 것이다.

'All > C++' 카테고리의 다른 글

자주 사용하는 STL function  (0) 2016.11.28
operator new / operator delete  (0) 2016.03.14
메모리 누수 검사  (0) 2015.12.29
가변인자 로그 출력함수  (0) 2015.12.29
RValue와 LValue  (2) 2015.12.28

+ Recent posts