블로그를 너무 오래 쉰 듯하다.. 다시 열심히 해야지

 

다시 시작하면서 포스팅할 주제는 스마트 포인터이다.

현재 개발하고 있는 시스템은 10년이상 전부터 개발된 것들이라 raw 포인터가 많지만 최근에 개발되는 코드에서는 스마트 포인터를 사용하는 추세인 것 같다.

C++ 11부터 도입되었다는데 왜 잘 사용되지 않았는지는 모르겠다.. 워낙 legacy가 많아서 그런걸까

최근에는 기존 기능을 수정하는 업무를 주로 하다보니 새로운 포인터를 다룰 일이 없었다.

다음에 기회가 되면 스마트 포인터를 사용해봐야겠다.

 

아무튼 아무래도 스마트 포인터를 사용하는 가장 큰 이유는 memory leak 방지라고 생각된다.

본인이 개발하는 시스템에서도 trouble report에서 memory leak이 심심찮게 발견되고 있다.

 

일단 첫 번째 스마트 포인터는 memory 헤더 파일에 정의되어 있다.

c++11부터 다음 세 가지의 스마트 포인터를 제공하고 있다.

 

1. unique_ptr

2. shared_ptr

3. weak_ptr


한 메모리를 바라보는 포인터들이 여러개인 경우 중간에 접근하는 포인터에서 메모리를 해제하는 경우 null 포인터 접근이 발생할 수도 있고 해제하지 않는 경우 memory leak이 발생할 수도 있다. raw 포인터는 이를 직접 delete를 사용해서 관리해야한다.

업무 중에 포인터를 전달하고 전달하다가 마지막에 delete를 하는 코드를 본적이 있다. 지금 생각해보면 비효율적이다..

추후에 멀티쓰레드 환경에서는 어떤 식으로 포인터를 관리하는지 찾아봐야겠다.

 

그리고 참조한 블로그에서 언급한 예로 예외등이 발생해서 delete 코드가 실행되지 않는 경우...

raw 포인터는 모든 예외 처리 구문에서 포인터를 해제해줘야겠지만 스마트 포인터는 객체가 제거되면서 메모리 해제가 실행이 된다고 한다.

 


메모리 관리 전략은 크게 두가지가 있다고 한다.

하나의 메모리를 접근하는 포인터는 무조건 하나만 존재하게 만드는 방식

해당 메모리를 바라보는 포인터가 증가하거나 감소할 때 카운트를 관리하는 방식 - 카운트가 0이 되면 메모리를 해제

 

1. unique_ptr

유니크 포인터는 첫 번째 고유 소유권 방식의 포인터이다.

유니크 포인터를 생성하면 다른 유니크 포인터를 선언하고 대입하는 것은 불가능하다.

언급했다시피 해당 메모리는 하나의 포인터만 접근할 수 있다. 만약 다른 포인터가 그 메모리에 접근하고 싶다면 move 연산자를 사용해야한다.

물론 move한 이후 첫 번째 포인터는 비어있을 것이다.

	auto ptr1 = std::make_unique<int>(1);
	cout << *ptr1 << endl;

    unique_ptr<int> ptr2 = move(ptr1);

	if (!ptr1)
	{
		std::cout << "ptr1 is empty" << std::endl;
	}

따라서 이 코드를 실행하면 ptr1 is empty가 출력된다.

 

 

2. shared_ptr

메모리를 바라보는 포인터들을 카운트해서 관리하는 방식이다.

따라서 shared_ptr 객체에 use_count라는 reference count 변수가 존재한다.

    auto ptr3 = std::make_shared<int>(11);
    cout << ptr3.use_count() << endl;
    shared_ptr<int> ptr4 = ptr3;
    cout << ptr3.use_count() << endl;
    ptr4.reset();
    cout << ptr3.use_count() << endl;

출력 결과는 다음과 같다.

1
2
1

 

굉장히 편리해보이지만 shared_ptr은 큰 단점이 하나 있는데 두 개의 다른 shared 포인터가 서로를 가리키고 있다면 카운트가 0이 될 수 없는 순환 참조가 발생하게 된다.

 

 

3. weak_ptr

weak_ptr은 shared_ptr의 단점인 순환 참조를 방지하기 위한 포인터로 shared_ptr를 관리하는 카운트에 포함되지 않는다.

따라서 위 코드에서 weak_ptr로 ptr3을 대입해도 reference count는 증가하지 않는다. 즉 shared_ptr의 객체의 수명에 관여하지 않는 포인터이다.(디버거로 살펴보면 strong/weak refCount가 따로 표시된다고한다.)

 

개념은 충분히 이해가 가는데 나중에 순환 참조 관련된 예제와 weak_ptr을 사용해서 해결하는 코드까지 정리를 한번 해야겠다.

'Language' 카테고리의 다른 글

[C++] std::generate, std::for_each  (0) 2022.05.08
[C++] stringstream - 문자열 처리  (0) 2022.04.04

STL 함수들은 워낙 정보가 많아 이 블로그에 굳이 정리를 해야할까 생각을 했지만

이 블로그의 시작은 내가 알게된 것들을 다시 정리하기 위함이 목적이었기 때문에

STL에 대한 내용도 계속해서 추가할 예정이다.

 

1. generate

algorithm 헤더에 정의되어 있다.

https://modoocode.com/318

https://modoocode.com/260

STL 관련해서 아주 정리가 잘 되어 있어 자주 애용하는 곳이다.

오늘 정리할 함수에 대해서도 참고를 했다.

 

generate 함수는 인자로 입력된 범위에 인자로 입력된 함수의 리턴 값으로 복사한다.

for_each도 마찬가지로 범위를 전달받고 각 원소에 대해 실행할 함수도 전달 받는다.(함수의 리턴 값은 무시된다.)

 

for문으로도 동일하게 구현할 수 있을 것 같지만 람다 함수까지 사용하여 구현한다면 코드가 매우 간단해진다.

 

예제

 

#include <algorithm>

int randF()
{
	return rand() % 2;
}

void printF(int n)
{
	cout << n << " ";
}

int main()
{
    vector<int> r(20);
	cout << "Implementation using for loop" << endl;
	for(int i=0; i<20; i++) {
		r[i] = randF();
	}
	for(int i=0; i<20; i++) {
		cout << r[i] << " ";
	}
	cout << endl;
	cout << "Implementation using STL and lambda function" << endl;
	generate(r.begin(), r.end(), []()->int {return rand() % 2;});
	for_each(r.begin(), r.end(), [](int n)->void {cout << n << " ";});

    return 0;
}

 

출력

Implementation using for loop
1 1 0 0 1 0 0 0 0 0 1 1 1 1 1 1 1 0 1 0
Implementation using STL and lambda function
1 0 0 1 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0

 

*for_each 글의 댓글에서 for_each 함수의 장점이 단순히 코드의 간결화뿐이라고 하는데...

만약 그렇다면 generate도 for문과 성능 차이가 전혀 없을 수도 있겠다.

이 부분은 정확히 잘 모르겠다...

 

*람다 함수는 업무에서도 사용해본 적이 별로 없어 추가로 포스팅할 예정이다.

 

'Language' 카테고리의 다른 글

[C++] Smart pointer  (0) 2022.06.20
[C++] stringstream - 문자열 처리  (0) 2022.04.04

최근에 이것 저것 준비하면서 자주 보이는 것들이 문자열 관련된 것들이었다.

학생 시절에는 대부분 C아니면 matlab을 사용했고 문자열을 다룰 일도 별로 없었다.

현재 회사에서도 문자열을 처리할 일이 없다... 는 변명이고

아무튼 문자열 처리를 좀 숙달할 필요가 있겠다 싶어 정리한다.

C++에서는 stringstream을 이용해서 문자열을 좀 더 쉽게 처리할 수 있었다.

string stream은 sstream header에 정의되어 있다.

참고 - https://caniro.tistory.com/187

https://myprivatestudy.tistory.com/48

 


다음과 같은 문자열이 들어오는 경우 각 단어를 분류하여 다음과 같이 출력하려면 어떻게 해야 할까

 

Input ->

"apple 10ea

coke 5can

orange 12ea

note 5ea

pen 20ea

coffe 13can"

 

Return ->

"Fruits 22ea

Beverage 18can

Stationary 25ea"

 


1. istringstream으로 input을 파싱한다.

2. 파싱된 string을 buffer에 insert한다. (while)

3. buffer를 분류한다.(과일, 문구, 음료)

4. flag를 세팅한다. 과일인지 문구인지...

5. flag를 보고 counting한다.

반복..

 

string solution(string input) {
    string answer;
    istringstream ss(input);
    string buf;
    int numFruit=0;
    int numBevarage=0;
    int numStatinary=0;
    pair<string,int> flag={"", 0};
    while(ss >> buf) {
        if(flag.second%2==0) {
            flag.first = buf;
        }
        else {
            if(flag.first == "apple" || flag.first == "orange") {
                buf.erase(buf.find("ea"));
                numFruit += stoi(buf);
            }
            if(flag.first == "note" || flag.first == "pen") {
                buf.erase(buf.find("ea"));
                numStatinary += stoi(buf);
            }            
            if(flag.first == "coke" || flag.first == "coffee") {
                buf.erase(buf.find("can"));
                numBevarage += stoi(buf);
            }
        }
        flag.second+=1;
        cout << "buf : " << buf << endl;
        cout << numFruit <<" "<< numBevarage <<" "<< numStatinary << endl;
    }


    return answer;
}

 

string의 find함수를 사용하여 숫자 뒤에 붙어있는 문자들을 지웠다.

다음에 코딩테스트에 string이 나오면 적절하게 사용해봐야겠다.

'Language' 카테고리의 다른 글

[C++] Smart pointer  (0) 2022.06.20
[C++] std::generate, std::for_each  (0) 2022.05.08

+ Recent posts