※ 참조 변수 요약
필자가 신입 때 부터 정말 많이 사용해온 참조 변수는 C++에서 사용 가능한 문법이다.(C언어는 안됨)
참조 변수란, 다른 변수를 참조하는 변수로써 주로 C언어의 포인터와 비교를 많이 하고는 한다. 주로 전달 인자를 함수에 넘길때 사용되며, 구조체나 클래스와 같이 덩치가 큰 타입에 사용하여 속도를 높인다. C언어에서는 문자열이나 구조체 등과 같이 덩치가 큰 타입을 포인터로 넘기지만 C++에서는 포인터 대신에 주로 참조를 사용한다. 
문법은 다음 포스팅부터 알아보도록 하겠다.

※ 참조 변수 특징
- 주 용도는 함수의 전달 인자(매개변수)에 사용한다.
- 구조체나 클래스와 같이 덩치가 큰 타입에 사용하기 적합하다.(덩치가 큰 타입을 복사해서 넘길 경우 느리기 때문)
- 참조 변수를 전달 인자로 사용하면 포인터처럼 복사본이 아닌 원본 데이터를 가지고 작업할 수 있다. (Call by reference)

※ 참조 변수와 포인터
- 포인터와 달리 참조자에는 NULL 참조라는 개념이 없다, 고로 선언될 때 반드시 초기화 해줘야 한다.
- 포인터는 중간에 가리키는 대상을 바꿀 수 있지만, 참조자는 const와 같아서 중간에 대상을 바꿀 수 없다.
- 참조자는 반드시 초기화 해줘야 하기 때문에 포인터처럼 사용하기 전, 유효성 검사를 할 필요가 없기에 더 효율적이다.

※ 참조 변수를 사용해야 하는 경우

- 참고하고자 하는 어떤 객체를 미리 알고 있을때

- 중간에 참조하는 대상을 바꿀 일이 결코 없을때

- 포인터를 사용하면 문법상 의미가 어색해지는 연산자를 구현할때(필자는 주로 STL 컨테이너를 사용할때임)


아래는 필자가 15년 09월 쯤에 제작한 클래스다.

operator>>들의 인자를 보면 죄다 참조 변수로 되어 있는데, 이처럼 참조는 클래스를 설계할 때도 필수적으로 사용된다.


※ 요약
이전에는 C언어로 두 점 사이의 거리를 구했는데, 같은 예제를 이번엔 C++로 작성해보았다.
사실 매일 하나의 글을 포스팅하기 위해 C언어와 C++을 나눠서 하느라 가끔 억지스러운 면이 없지 않아 있는거 같다.

※ 특징
C++을 이용해 피타고라스 정리를 구현하여 두 점 사이의 거리를 구한다.
좀 더 자세한 설명은 이전 글에 있으니 필요하다면 참고하기 바란다.

큰 틀은 C언어로 작성한 것과 같다. template 클래스와 template 함수로 작성하여 타입에 상관없이 거리를 구할 수 있게 하였다.
이렇게 template으로 만든 이유는, 윈도우 클라이언트 영역은 정수 타입이고, OpenGL이나 DirectX는 보통 실수 타입이기 때문이다.
또 필자는 아직 template 문법에 약하기 때문에 조금씩이나마 연습을 하고자 아래와 같이 작성하였다.


아래 화면은 상기 코드에 대한 디버깅 화면이다.

p1은 실수 타입으로, p2는 정수 타입으로 찍힌걸 보니 마음에 든다.

위 소스코드는 2010 기준으로 만들었다, 2013 이상이라면 GetDistance에서 double형으로 형변환을 안해줘도 에러가 발생하지 않는다.




#include <cmath>

template<typename T>
class CPoint
{
public:
	T x;
	T y;

public:
	CPoint( ) : x(0), y(0) { }
	CPoint( T _x, T _y ) : x(_x), y(_y) { }
};

template<typename T1, typename T2>
double GetDistance( const T1& p1, const T2& p2 )
{
	return std::sqrt( double( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) ) );
}

int main( )
{
	CPoint<double> p1( 432, 189 );
	CPoint<int> p2( 38, 392 );

	double dDistance = GetDistance( p1, p2 );

	return 0;
}


 

※ 로또 프로그램 요약
얼마 전 C++11 문법을 이용한 로또 프로그램을 올렸는데, 그 때 올린건 이전에 만든 C 스타일과의 비교를 위해 class를 이용하지 않았었다. 이번에 만든 건 class를 이용하여 만든 버전이다. 더불어 Sunny Holic님이 언급하신 std::shuffle 방식과 random 방식의 속도 비교도 해보았다.

※ 로또 프로그램 특징
- 이전에 만들었던 것과 다르게 class를 이용하여 제작하였다.
- random 방식과 std::shuffle 방식을 이용하여 번호를 생성하며, 두 방법의 속도를 비교 하였다.

아래는 예제 소스코드다, 소스코드는 첨부해뒀으니 Visual Studio 2013 이상이라면 바로 컴파일해 볼 수 있다.
- header file


main.cpp


아래는 속도를 비교한 것이다, 속도는 random 방식이 빠르지만 구현은 shuffle 방식이 더 간단하다는 장점이 있다.

또 random 방식은 다른 곳에 응용(더 많은 수를 중복 없이 생성하는 곳)해도 빠른 속도를 보장 받을 수 있다는 장점이 있다.



 

※ std::shared_ptr 요약
C++11에 새로 추가된 스마트 포인터인 shared_ptr에 대한 내용이다.

스마트 포인터란, 사용하지 않는 메모리나 사용이 끝난 메모리를 자동으로 해제/관리 해주는 것을 의미하는 것으로써 JavaC#의 가비지 컬렉터와 비교 했을때 성능의 손해는 거의 없으면서 메모리 관리의 이점을 얻을 수 있으므로 편하고도 안전하다.

스마트 포인터는 메모리 누수 관리에 자신이 없는 초보자들이나 복잡하고 거대한 프로젝트를 진행할때 사용하면 적당할 거 같다.

그렇지만 필자는 스마트 포인터를 거의 사용하지 않는다. 아직은 직접 할당, 해제를 해줘야 안심?이 되기 때문이다.


※ std::shared_ptr 특징
- 레퍼런스(참조) 카운팅을 지원한다.
- 명시적인 delete를 해주지 않아도 마지막에 참조하는 곳에서 스코프를 벋어날때 자동으로 해제된다.
- 특별히 어려운 문법을 배우지 않고도 메모리 관리에 큰 편의를 제공받는다.

※ std::shared_ptr을 사용해야 하는 경우

- 메모리 관리에 자신없는 분들이 메모리를 관리해야 할 때

- 거대하고 복잡한 프로젝트를 진행 할 때

- C++11이나 그 이상을 지원하는 컴파일러를 사용 할 때


※ 로또 프로그램 요약
C++11 문법을 이용하여 제작한 간단한 로또 프로그램이다.

전에 C언어로 제작한 로또 프로그램을 C++11 문법으로 리팩토링 하였다.

알고리즘 및 큰 흐름은 거의 같고 문법만 C에서 C++11로 바꿨으니 둘을 비교하면서 보면 좋을 것이다.


※ 로또 프로그램 특징
- C++11에 추가된 새로운 random(Mersenne Twister)을 이용하여 속도가 빠르고 품질이 좋다.

- 중복 판별을 위한 인덱스를 따로 관리하여 요소 개수가 많더라도 중복 판별에 상수 시간 복잡도를 갖을 수 있어 매우 빠르다.

- 표준 문법으로만 작성하여 윈도우, 리눅스 모두 컴파일 가능하다. (단 VS2013(12.0) 이상, gcc 4.8 이상)

 

#include <iostream>	//std::cout
#include <random>		//std::mt19937_64
#include <array>		//std::array
#include <algorithm>	//std::sort
#include <functional>	//std::greater

#define _NUMBER_MAX 6
#define _LOTTTO_MAX 45

int GetNumber( const int nMin, const int nMax );
std::ostream& operator<<( std::ostream& ostr, const std::array<int, _NUMBER_MAX>& arr );

int main( )
{
	//변수 선언
	std::array<int, _NUMBER_MAX> nNumber = { 0, };	//생성된 번호가 담길 배열
	std::array<bool, _LOTTTO_MAX> bIndex = { false, };	//빠른 중복 판단을 위한 배열 

	//번호 생성
	for( unsigned int i=0 ; i<nNumber.size() ; )
	{
		const auto nTempNum = GetNumber( 1, _LOTTTO_MAX );	//랜덤으로 번호 생성

		if( bIndex[nTempNum-1] == false )			//생성된 번호와 인덱스를 비교
		{
			bIndex[nTempNum-1] = true;			//생성된 번호와 인덱스 값 변경
			nNumber[i++] = nTempNum;			//생성된 번호 삽입
		}
	}

	//정렬, 주석을 지우면 내림차순 정렬한다.
	std::sort( nNumber.begin(), nNumber.end()/*, std::greater<int>( )*/ );
	
	//출력
	std::cout<<nNumber;

	return 0;
}

int GetNumber( const int nMin, const int nMax )
{
	std::random_device rn;		//시드 값
	std::mt19937_64 rnd( rn() );	//random_device를 시드 값으로 하여 초기화

	//랜덤의 범위를 지정
	std::uniform_int_distribution<int> nDist( nMin, nMax );
	
	return nDist( rnd );
}

std::ostream& operator<<( std::ostream& ostr, const std::array<int, _NUMBER_MAX>& arr )
{
    for( auto &i : arr )
    {
        ostr<<" "<<i;
    }

    return ostr;
}

 

 

 

※ 요약

std::list의 주요 멤버 함수 목록이다.

C++03과 C++11의 구분을 색으로 했으며, 추후에 추가되는 C++14, C++17에 대해서도 추가하도록 하겠다.
std::list의 각 멤버 함수들의 본격적인 사용법을 알아보기 전에 한 번 훑어보면 도움이 될 것이다.

 

- C++03

- C++11

 분 류

 멤버 함수

 설 명

Iterators

begin

 첫 번째 원소를 가리키는 반복자를 리턴한다.

cbegin

 첫 번째 원소를 가리키는 상수 반복자를 리턴한다.

end

 마지막 원소를 가리키는 반복자를 리턴한다.

cend

 마지막 원소를 가리키는 상수 반복자를 리턴한다.

rbegin

 역 순차열의 첫 번째 원소를 가리키는 반복자를 리턴한다.

crbegin

 역 순차열의 첫 번째 원소를 가리키는 상수 반복자를 리턴한다.

rend

 역 순차열의 마지막 원소를 가리키는 반복자를 리턴한다.

crend

 역 순차열의 마지막 원소를 가리키는 상수 반복자를 리턴한다.

Element access

at

 제공 안 함

operator[]

 제공 안 함

front

 첫 번째 원소의 참조를 리턴한다.

back

 마지막 원소의 참조를 리턴한다.

Capacity

empty

 원소 존재 유무를 체크한다. 아무것도 없으면 true, 있으면 false를 리턴한다.

size

 원소의 개수를 리턴한다.

max_size

 담을 수 있는 원소의 최대 개수를 리턴한다.

resize

 list의 크기를 변경하고 default 값이나 임의 값으로 초기화한다.

capacity

 제공 안 함

reserve

 제공 안 함

shrink_to_fit

 제공 안 함

Modifiers

clear

 list의 모든 원소를 제거한다.

assign

 기존 원소들은 모두 제거 후, 임의 값으로 n개의 원소를 할당한다.

insert

 임의 위치에 임의 값을 삽입한다.

emplace

 원소 삽입시 컨테이너 내부에서 생성 후 임의 위치에 임의 값을 삽입한다.

emplace_hint

 제공 안 함

erase

 임의 위치의 원소나 지정 범위의 원소를 삭제한다.

push_front

 list의 처음에 원소를 추가한다.

emplace_front

 원소 삽입시 컨테이너 내부에서 생성 후 컨테이너의 처음에 원소를 추가한다.

pop_front

 list의 처음 원소를 제거한다.

push_back

 list의 끝에 원소를 추가한다.

emplace_back

 원소 삽입시 컨테이너 내부에서 생성 후 컨테이너의 끝에 원소를 추가한다.

pop_back

 list의 마지막 원소를 제거한다.

swap

 list1.swap( list2 )일때 list1과 list2를 swap한다.

List operations

merge

 list1.merge( list2 )일때 list1에 list2를 정렬하면서 병합한다.

splice

 2개의 list 중 인자로 주어지는 list의 지정된 원소들을 대상 list로 이동시킨다.

remove

 인자로 받은 값으로 받은 값과 같은 값의 원소를 모두 제거한다.

remove_if

 함수객체의 조건을 만족하는 원소를 모두 제거한다.

reverse

 list에 담긴 원소의 순서를 역순으로 바꾼다.

unique

 list에 담긴 원소 중 연속적으로 중복된 값이 배치된 원소를 제거한다.

sort

 list에 담긴 원소를 정렬한다. 예 : list.sort( std::greater<int>() )

Lookup

count

 제공 안 함

find

 제공 안 함

lower_bound

 제공 안 함

upper_bound

 제공 안 함

equal_range

 제공 안 함

Observers

key_comp

 제공 안 함

value_comp

 제공 안 함

hash_function

 제공 안 함

key_eq

 제공 안 함

Allocator

get_allocator

 list의 allocator 객체를 반환한다.



 

 

'STL - Containers > list' 카테고리의 다른 글

[C++ STL] std::list 개요  (0) 2015.11.11

 

※ 요약
std::vector의 주요 멤버 함수 목록이다.
오버로딩 되는 항목들에 대해서 모두 넣지 않았지만 대략적인 설명을 할 것이므로 큰 문제는 없을 것이다.
또 한 C++03과 C++11의 구분을 색으로 할 것이며, 추후에 추가되는 C++14, C++17에 대해서도 추가하도록 하겠다.
std::vector의 본격적인 사용법을 알아보기 전에 한 번 훑어보면 도움이 될 것이다.

- C++03

- C++11

 분 류

 멤버 함수

 설 명

Iterators

begin

 첫 번째 원소를 가리키는 반복자를 리턴한다.

cbegin

 첫 번째 원소를 가리키는 상수 반복자를 리턴한다.

end

 마지막 원소를 가리키는 반복자를 리턴한다.

cend

 마지막 원소를 가리키는 상수 반복자를 리턴한다.

rbegin

 역 순차열의 첫 번째 원소를 가리키는 반복자를 리턴한다.

crbegin

 역 순차열의 첫 번째 원소를 가리키는 상수 반복자를 리턴한다.

rend

 역 순차열의 마지막 원소를 가리키는 반복자를 리턴한다.

crend

 역 순차열의 마지막 원소를 가리키는 상수 반복자를 리턴한다.

Element access

at

 n번째 원소를 참조할 때 사용하며 범위 점검을 하므로 []보다 느리다.

operator[]

 n번째 원소를 참조할 때 사용하며 범위 점검을 안하므로 at보다 빠르다.

front

 첫 번째 원소의 참조를 리턴한다.

back

 마지막 원소의 참조를 리턴한다.

Capacity

empty

 원소 존재 유무를 체크한다. 아무것도 없으면 true, 있으면 false를 리턴한다.

size

 원소의 개수를 리턴한다.

max_size

 담을 수 있는 원소의 최대 개수를 리턴한다.

resize

 vector의 크기를 변경하고 default 값이나 임의 값으로 초기화한다.

capacity

 vector에 할당된 메모리의 크기를 리턴한다.

reserve

 지정한 크기 만큼의 메모리를 미리 할당한다.

shrink_to_fit

 사용되지 않는 capacity size를 제거한다. 즉 size() == capacity()가 된다.

Modifiers

clear

 vector의 모든 원소를 제거한다.

assign

 기존 원소들은 모두 제거 후, 임의 값으로 n개의 원소를 할당한다.

insert

 임의 위치에 임의 값을 삽입한다.

emplace

 원소 삽입시 컨테이너 내부에서 생성 후 임의 위치에 임의 값을 삽입한다.

emplace_hint

 제공 안 함

erase

 임의 위치의 원소나 지정 범위의 원소를 삭제한다.

push_front

 제공 안 함

emplace_front

 제공 안 함

pop_front

 제공 안 함

push_back

 vector의 끝에 원소를 추가한다.

emplace_back

 원소 삽입시 컨테이너 내부에서 생성 후 컨테이너의 끝에 원소를 추가한다.

pop_back

 vector의 마지막 원소를 제거한다.

swap

 v1.swap( v2 )일때 v1과 v2를 swap한다.

List operations

merge

 제공 안 함

splice

 제공 안 함

remove

 제공 안 함

remove_if

 제공 안 함

reverse

 제공 안 함

unique

 제공 안 함

sort

 제공 안 함

Lookup

count

 제공 안 함

find

 제공 안 함

lower_bound

 제공 안 함

upper_bound

 제공 안 함

equal_range

 제공 안 함

Observers

key_comp

 제공 안 함

value_comp

 제공 안 함

hash_function

 제공 안 함

key_eq

 제공 안 함

Allocator

get_allocator

 vector의 allocator 객체를 반환한다.




'STL - Containers > vector' 카테고리의 다른 글

[C++ STL] std::vector - emplace_back  (3) 2016.04.15
[C++ STL] std::vector - push_back  (0) 2016.02.19
[C++ STL] std::vector 개요  (3) 2015.11.10

 

※ std::array 요약
std::array는 C++11에 새롭게 추가된 컨테이너 라이브러리로써, 크기가 고정되어 있다는 것을 제외하면 std::vector와 비슷하다.
크기가 고정이므로 스택에 저장되며 C++03까지는 없었던 고정 길이 배열 자료구조 컨테이너로써 기존의 배열을 대체하기에 완벽하다.

※ std::array 특징
- 배열을 대체할 수 있다.
- 정적 배열이므로 스택에 저장된다. (std::vector는 힙 메모리에 저장된다!)
- 데이터가 연속된 위치에 저장되므로 랜덤 액세스가 가능하고 접근 속도도 빠르다.
- 정적이므로 push_back(), pop_back(), insert(), erase(), clear(), resize(), reserve(), capacity()멤버 함수는 지원하지 않는다.

※ std::array를 사용해야 하는 경우
- C++11이 지원되는 컴파일러라면 배열 대신 사용하면 좋다.
(필자는 C++11이 지원되는 컴파일러에서 작업하면 배열 대신 무조건 std::array를 사용한다. STL과 새롭게 추가되는 문법 등과의 결합도가 좋고 속도도 빠르며, 무엇보다 (조금이지만)편하기 때문이다.)


 

※ std::deque 요약
std::deque는 Double Ended Queue의 약자로써, 시퀀스 컨테이너다.

필자는 시뮬레이터 서버를 제작할 때와 Singleton 방식의 Log Class를 제작할때 std::deque의 사용을 고려해본 적이 있다.


※ std::deque 특징

- 크기가 가변적이다. (사실 std::array를 제외한 거의 모든 STL 컨테이너의 특징이다.)

- 앞(front)과 뒤(back)에서의 삽입, 삭제 성능은 좋지만 중간에서의 삽입, 삭제는 그다지 좋지 않다.

- std::deque는 Stack과 Queue가 결합된 자료구조로써 연결 리스트보다 구현하기가 어렵다.

- std::vector처럼 랜덤 액세스가 가능하다.


※ std::deque와 std::vector
- std::deque와 std::vector의 멤버 함수 기능이나 사용 방법은 거의 동일하다.

- std::vector는 뒤(back)에서의 삽입, 삭제 성능이 좋지만 std::deque는 앞(front), 뒤(back), 모두에서 좋다.

- std::deque의 앞, 뒤 삽입, 삭제 성능을 제외한 다른 위치에서의 삽입, 삭제는 std::vector가 더 좋다.

- 둘 다 랜덤 액세스가 가능하다.

 

※ std::deque를 사용해야 하는 경우

- 앞과 뒤에서 삽입, 삭제를 자주 할 때

- 저장할 데이터의 개수가 가변적 일 때

- 데이터 검색을 거의 하지 않거나, 아예 안 할 때 (검색을 해야 한다면 std::map이나 std::set 등의 연관 컨테이너 사용을 고려)

- 랜덤 액세스를 해야 할때

- 서버처럼 받은 패킷을 차례대로 처리할 때 (이 때는 범용적인 deque보다 좀 더 좋은 성능의 독자적인 자료구조를 만들기도 함)


 

 

 

※ STD::forward_list 요약
std::list가 양방향(double linked list)이라면 std::forward_list는 단방향(single linked list)이다.
단방향 연결 리스트라 양방향인 std::list보다 메모리 사용량이나 처리 속도면에서 조금 유리하다.
double linked list가 필요하다면 std::list를, single linked list로도 충분하다면 std::forward_list를 사용한다.

※ std::forward_list 특징
- single linked list(단일 연결 리스트) 자료구조를 이용하여 만든 시퀀스 컨테이너다.
- 설계 디자인을 std::list에 맞춘다.(STL은 범용성을 지향하므로 다른 컨테이너들도 마찬가지다.)
- 속도와 사이즈, 즉 성능을 최우선으로 설계, 제작되었다.
- 다른 STL 컨테이너에 있는 size 함수를 제공하지 않는다.
- size는 다음과 같이 구한다. -> size_t size = std::distance( list.begin(), list.end() )

※ std::forward_list와 std::list
- std::list는 양방향으로 이동 가능하지만 std::forward_list는 한 방향으로만 이동할 수 있다.
- std::list보다 삽입/삭제 속도가 아주 약간 더 빠르다.
- 양방향인 std::list에 비해 메모리를 적게 사용한다. 각 요소 뿐만이 아닌, 컨테이너 자체 사이즈도 더 작다.
- 삽입과 삭제는 지정한 요소의 다음 요소에 한해서만 가능하다.(단방향이므로 당연하다)
- 구현의 복잡성과 성능 문제 때문에 std::list에서 제공하는 insert와 erase를 제공하지 않는다.

 

※ std::forward_list를 사용해야 하는 경우

- double linked list가 아닌 single linked list로도 충분할때


 

+ Recent posts