
^.^
by 저녁참새
메뉴릿
카테고리
|
dll 그리고 class현재 개인 프로젝트를 진행하면서, dll을 만들어야 할 필요가 생겼는데,
dll의 내부는 전부다 C++로 구현을 했습니다.
그러다 보니, dll의 헤더 파일에서 C++ class의 포인터를 넘겨주는 구조로 만들거나,
아니면 class의 인터페이스를 전부 C 스타일로 래핑(Wrapping)을 해야하는 상황이 벌어졌습니다.
Windows Api 정복 2권 44장에 보면 C++ 클래스를 dllexport하는 방법은 써 있는데, 이것을 어떻게 Wrapping하는지는 안 써있습니다. (Wrapping하는 것이 그냥 class를 쓰는 것보다는 좋다고 나와있기는 합니다.... 예시도 있었으면 더 좋았을 것을....) 그래서 Google 검색 결과를 통해 배운 것을 여기에 정리하고자 합니다.
Class 인터페이스를 C 스타일로 Wrapping하기위 링크는 제가 이 작업을 하는데 가장 큰 도움을 준 글입니다. 지금 아래에 적는 방법은 위 글의 내용을 약간 변형한 것입니다.
솔루션 구성우리가 실습할 솔루션은 두 개의 프로젝트로 구성이 되어 있습니다. 1. Money : 그냥 간단히 작동하는가 출력을 위해서 console application으로 만들었습니다.
2. MoneyDLL : Money라는 클래스로 구현된 DLL입니다.
소스코드 보기 MoneyDLL/* money.h */
#pragma once
class Money { public: int test(); int test2(); }; |
/* money.cpp */ #include "Money.h"
int Money::test() { return 0; }
int Money::test2() { return 1; } |
설명이 필요 없을만큼 참으로 간단한 코드입니다.
과연 class로 dll을 구현했을 경우 어떤 일이 벌어질 것인가를 알기 위해서 적은 코드입니다.
/* MoneyHeader.h */ #ifdef DLLEXPORT #define MYDLLTYPE __declspec(dllexport) #else #define MYDLLTYPE __declspec(dllimport) #endif
#include "Money.h"
typedef void* PMoney; // ⓐ
// ⓑ extern "C" MYDLLTYPE PMoney MoneyConstruct(); extern "C" MYDLLTYPE int MoneyTest(PMoney money); extern "C" MYDLLTYPE int MoneyTest2(PMoney money); extern "C" MYDLLTYPE void MoneyDestruct(PMoney money); |
여기서는 두 군데에 집중을 해 보도록 합시다.
ⓐ 부분: 여기에서는 우리가 사용할 자료형을 정의하는 것입니다.
Money에 대한 포인터이므로 PMoney라고 정의하였습니다.
void*을 계속 사용하는 것보다는 아무래도 가독성면에서 이득이 클 것이라고 생각이 됩니다.
ⓑ 함수들 : 인터페이스들을 모아 놓았습니다.
생성자와 소멸자는 MoneyConstruct()와 MoneyDestruct(PMoney)로 만들어졌습니다.
생성자 이외의 함수에서는 모두 PMoney를 받고 있습니다.
/* MoneyImpl.cpp */ #define DLLEXPORT #include "MoneyHeader.h"
extern "C" MYDLLTYPE PMoney MoneyConstruct() { Money* money = new Money(); return (PMoney)money; }
extern "C" MYDLLTYPE int MoneyTest(PMoney money) { Money* m = (Money*)money; return m->test(); }
extern "C" MYDLLTYPE int MoneyTest2(PMoney money) { Money* m = (Money*)money; return m->test2(); }
extern "C" MYDLLTYPE void MoneyDestruct(PMoney money) { Money* m = (Money*)money; delete m; } |
위의 MoneyHeader.h에 선언된 함수들에 대한 정의입니다. 보시면 아시겠지만,
그냥 연결해주는 것 외에는 딱히 하는 일이 없습니다.
Money/* main.cpp */ #include <stdio.h> #include "../MoneyDLL/MoneyHeader.h"
#pragma comment(lib, "MoneyDLL")
void main() { PMoney m = MoneyConstruct(); printf("%d %d", MoneyTest(m), MoneyTest2(m)); MoneyDestruct(m); } |
정상 작동하는가 실험을 해 보았습니다. 0, 1을 출력합니다.
정확히 잘 동작합니다.
바보같은 질문이란 없습니다. Q: 돈과 관련이 전혀 없는데, 왜 클래스 이름이 Money인가요?A: 요즘 UnitTest++이라는 단위 검사 Framework를 공부하다 보니, TDD(테스트 주도 개발)에서 예제로 삼는 Money 예제를 자주 보게 되었습니다.
거기서 Money 클래스를 자주 쓰길래 Money라고 이름을 지어 봤습니다.
딱히 다른 이유가 있었던 것은 아닙니다.
이 네이밍 규칙은 닷넷 스파이더 팀에서 만든 [C# Coding Standards and Best Programming Practices]를 바탕으로 만들었습니다. 1. Namming Rule 이 문서 전반에 걸쳐 파스칼표기법과 카멜 표기법이 사용된다.
파스칼 표기법 - 모든 단어에서 첫번째 문자가 대문자이며 나머지는 소문자이다.
예: BackColor
카멜 표기법 - 최초에 사용된 단어를 제외한 첫번째 문자가 대문자이며 나머지는 소문자이다.
예: backColor
|
- 클래스 명에는 파스칼 표기법을 사용한다.
public class HelloWorld { ... }
|
- 함수(Method) 명에는 파스칼 표기법을 사용한다.
void SayHello(string name) { ... }
|
- 변수와 함수 파라미터에는 카멜표기법을 사용한다.
int totalCount = 0;
void SayHello(string name) { string fullMessage = "Hello " + name; ... }
|
- 인터페이스에는 접두사 "I"가 포함된 파스칼표기법에 따른다. ( Example: IEntity )
- 변수명에 헝가리안 표기법을 사용하지 않는다.
아래와 같은 방법은 사용하지 않는다.
string m_sName; int nAge; |
- 변수에 모든 의미를 충분히 담아라. 약어를 사용하지 말것.
좋은 예: string address int salary
나쁜 예: string nam string addr int sal |
- i, n, s 같이 한글자로 된 이름을 사용하지 말것. index, temp 같은 이름을 사용할 것. 아래의 경우에 한하여 허용할 수 있음
변수가 반복의 의미만 가지고 있고 for문이 10줄 내외인 경우
for (int i = 0; i < count; i++) { ... } |
- 지역변수에는 밑줄(_)을 사용하지 않는다.
- 모든 멤버 변수들은 앞에 밑줄(_)을 사용해야 한다. 그래야 다른 지역변수들과 구분할 수 있다.
- 키워드와 비슷한 이름을 하용하지 말것.
- boolean 이 들어가는 변수, 속성, 함수(method)등은 "is" 또는 "has"를 사용한다.
예: private bool _isFinished bool IsName(string name) { bool hasGood; ... }
|
- 네임스페이스 명은 사내에서 정한 표준 패턴을 따라야 한다. 표기법은 파스칼 표기법을 따른다.
<회사명>.<제품명>.<최상위모듈>...<하위모듈> namespace Innotive.ACG.Main
|
- UI 구성요소는 UI 컨트롤 이름 뒤에 의미가 있는 이름을 붙인다. 대소문자는 UI 구성요소가 제공하는 이름을 따른다.
예 : Button : ButtonConfirm Label : LabelName DataList : DataListOption
|
파일명은 클래스 명과 같아야 한다.
클래스 명이 "HelloWorld" 라면 파일명은 "HelloWorld.cs" 이어야 한다. UI Clase와 논리 Class를 구분하기 위해 폼을 가지고 있는 클래스는 Form을 붙여준다. Class : "FormMain" File : "FormMain.cs" | 클래스 명이 파스칼표기법을 따르므로 파일명도 같이 파스칼 표기법을 따른다.
컨트롤 선언시 이름 앞에 x를 항상 붙인다.
<Button x:Name="xMyButton" /> |
이벤트 선언시 이름 앞에 e를 항상 붙인다.
public event EventHandler<AlarmListControlFocusListChangedEventArgs> eFocused; |
2. 들여쓰기 - 들여쓰기에는 TAB 을 사용한다. SPACE를 사용하지 않는다. Tab 사이즈는 4로 정의한다.
- 주석은 코드와 같은 레벨에 있어야 한다.(들여쓰기의 레벨을 같이 사용한다)
좋은 예: // Format a message and display string fullMessage = "Hello " + name; DateTime currentTime = DateTime.Now; string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString(); MessageBox.Show(message );
좋지 않은 예: // Format a message and display string fullMessage = "Hello " + name; DateTime currentTime = DateTime.Now; string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString(); MessageBox.Show ( message );
|
- 중괄호는 중괄호 밖에 있는 코드와 같은 레벨에 있어야 한다.
- 논리적인 코드 그룹은 다른 코드와 한칸 띄어서 구분한다.
착한 예: bool SayHello(string name) { string fullMessage = "Hello " + name; DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
MessageBox.Show (message);
if (...) { // Do something // ...
return false; }
return true; }
나쁜 예: bool SayHello(string name) { string fullMessage = "Hello " + name; DateTime currentTime = DateTime.Now; string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString(); MessageBox.Show (message); if (...) { // Do something // ... return false; } return true; }
|
- 클래스안에 함수 간에는 한칸씩 빈 라인을 집어 넣는다.
- 중괄호는 다른 라인과 분리되어 있어야하며 라인을 같이 쓰면 안된다.
착한 예: if ( ... ) { // Do something }
나쁜 예: if ( ... ) { // Do something }
|
- 지시자(operator)와 괄호 앞뒤로는 한 칸의 공간을 남긴다. "("와 ")"의 앞뒤로는 공간을 남기지 않는다.
착한 예: if (showResult == true) { for (int i = 0; i < 10; i++) { // } }
나쁜 예: if(showResult==true) { for(int i= 0;i<10;i++) { // } }
|
- 멤버변수는 파일의 최상단에 그 다음엔 property를 그리고 맨 뒤에 함수가 위치하도록 한다.
|
지금까지 STL 의 여러가지 기능에 대해서 알아보았는데, 이러한 기능들은 결국 STL 을 이용한 자료구조로써의 기능에 촛점이 맞추어져 있음을 알수 있다. STL은 이러한 자료구조를 쉽고 유연하게 구현해주기 위한 도구라고 할수 있을것이다.
자료구조가 사용되면 빠지지 않고 따라다니는게 있는데, 바로 알고리즘 들이다. (아마도 가장 많이 사용되는게 sort, search 과 같은 알고리즘들일것이다.)
STL 에서는 여러가지 컨테이너들로 구성된 자료구조의 연산을 위한 다양한 알고리즘을 제공한다.
알고리즘을 사용하기 위해서는 다음과 같이 헤더파일을 선언해 주어야 한다.
STL에서 제공하는 알고리즘들은 그 특성에 따라서 몇가지 종류로 분류할수 있다. "Non-mutating algorithms", "Mutating algorithms", "Sorting algorithms", Generalized numeric algorithms" 등이다.
이제 부터 이러한 분류별로 각각의 분류에 포함된 알고리즘을 설명하도록하겠다. 그러나 워낙에 많은 알고리즘이 존재하고 (대략 50여가지) 이들 알고리즘중에는 거의 사용되어지지 않는 알고리즘들도 있으므로, 자주 사용된다고 생각되는 알고리즘들만을 설명하도록 할것이다. 나머지 알고리즘에 대한 정보를 알기를 원한다면 SGI STL 홈 문서를 참고하길 바란다.
mutating 은 "변화", "변경" 의 뜻을 가진다. None-mutating algorithms 에 포함되는 알고리즘들은 컨테이너들의 원소에 대해서 None-mutating 즉 컨테이너를 변경시키지 않는 특성을 가지고 있다.
예를들어서 search 나, 원소의 갯수를 세는 count 등의 알고리즘은 컨테이너에서 필요한 원소를 찾거나, 계수를 하는 일을 할뿐으로, 컨테이너를 변경(원소를 삭제하거나, 위치를 변경시키거나)하는 일을 수행하지 않을것이다. 그런이유로 이러한 알고리즘들을 None-mutating(변경불가) 알고리즘이라고 부른다. (말은 무지 복잡한데 개념은 무지 간단하다는걸 알수 있을것이다.)
아마도 가장많이 사용되는 알고리즘일 것이다. 컨테이너에서 일정한 범위의 원소들사이에서 원하는 값을 찾는다. 이 범위는 iterator 로 주어진다.
template<class InputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
|
돌려주는 값은 역시 iterator 이며, 이 iterator 은 찾은 원소가 컨테이너에서 위치하는 값을 가리킨다.
#include <algorithm>
#include <vector>
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(7);
V.push_back(5);
vector<int>::iterator mi;
mi = find(V.begin(), V.end(), 4);
cout << *mi << endl;
}
|
count 는 말그대로 컨테이너에 포함된 원소의 갯수를 계수하기 위해서 사용된다.
iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last,
const EqualityComparable& value);
|
첫번째와 두번째 아규먼트로 주어지는 iterator 를 순환하면서 세번째 아규먼트로 주어지는 value 와 일치하는 값이 있는지 확인하여서 이 값을 계수한다.
#include <algorithm>
#include <vector>
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(1);
V.push_back(7);
V.push_back(1);
cout << count(V.begin(), V.end(), 1) << endl;
}
|
이것들은 변경가능한 알고리즘들로써, 컨테이너의 원소를 수정할수 있다. 컨테이너 원소를 복사하거나, 교체, 삭제 하는등의 작업을 수행한다. 이러한 작업 수행중에 원소의 내용이 변경되므로 Mutating 알고리즘이라고 한다.
copy 는 Iterator 로 주어지는 범위의 원소를 다른 컨테이너로 복사하고자 할때 사용한다.
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result);
|
첫번째 아규먼트와 두번째 아규먼트로 주어지는 Iterator 사이를 순환하며, 이들 Iterator 사이의 원소들을 result 이테레이터로 복사한다.
#include <algorithm>
#include <vector>
#include <list>
int main()
{
vector<int> V;
list<int> L(5);
V.push_back(1);
V.push_back(2);
V.push_back(3);
V.push_back(4);
V.push_back(5);
copy(V.begin(), V.end(), L.begin());
list<int>::iterator mi = L.begin();
while(mi != L.end())
{
cout << *mi << endl;
*mi++;
}
}
|
list 를 선언할때 크기를 5로 지정한걸 주목하라. copy 알고리즘은 자동적으로 컨테이너의 크기를 할당시켜주지 않으므로, 미리 복사될 컨테이너의 크기를 지정해 놓아야 한다. list 를 크기를 할당시키지 않고 copy 해보면 컨테이너의 크기가 할당되지 않으므로, 하나의 값만이 복사됨을 알수 있을것이다. 이럴경우 list 컨테이너에는 5 하나만 들어가게 될것이다.
swap 는 인자로 주어진 2개의 컨테이너의 원소를 교환하기 위해서 사용된다.
template <class Assignable> void swap(Assignable& a, Assignable& b);
|
다음은 swap 를 사용한 간단한 예이다.
#include <algorithm>
#include <vector>
int main()
{
vector<int> V1;
vector<int> V2;
V1.push_back(3);
V1.push_back(2);
V2.push_back(100);
V2.push_back(200);
swap(V1, V2);
cout << V1[0] << endl;
cout << V2[0] << endl;
cout << V2[1] << endl;
}
|
컨테이너의 원소를 뒤바꾼다. 즉 첫번째 원소는 마지막으로, 마지막원소는 첫번째로 교환하게 된다. 이러한 과정이 아규먼트로 주어진 iterator 사이의 모든 원소에 대해서 발생하게 된다.
template <class BidirectionalIterator> void reverse(BidirectionalIterator first, BidirectionalIterator last);
|
#include <algorithm>
#include <vector>
int main()
{
vector<char> V;
V.push_back('a');
V.push_back('b');
V.push_back('c');
V.push_back('d');
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
// a b c d 가 출력된다.
cout << endl;
reverse(V.begin(), V.end());
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
// d c b a 가 출력된다.
cout << endl;
}
|
iterator 범위내에서 일치되는 특정 원소를 삭제하고자 할때 사용한다.
template <class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const T& value);
|
#include <algorithm>
#include <list>
int main()
{
list<int> L;
L.push_back(1);
L.push_back(2);
L.push_back(5);
L.push_back(1);
L.push_back(3);
copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// 1 2 5 1 3 이 출력될것이다.
list<int>::iterator mi = remove(L.begin(), L.end(), 1);
copy(L.begin(), mi, ostream_iterator<int>(cout, " "));
cout << endl;
// 2 5 3 이 출력될것이다.
}
|
꽤 재미있는 알고리즘이다. 컨테이너에 포함된 원소를 랜덤하게 위치를 재배열 해주는 일을한다.
template <class RandomAccessIterator> void random_shuffle(RandomAccessIterator first, RandomAccessIterator last);
|
#include <algorithm>
#include <vector>
int main()
{
vector<char> V;
V.push_back('a');
V.push_back('b');
V.push_back('c');
V.push_back('d');
V.push_back('e');
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
cout << endl;
random_shuffle(V.begin(), V.end());
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
cout << endl;
random_shuffle(V.begin(), V.end());
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
cout << endl;
random_shuffle(V.begin(), V.end(), 2);
copy(V.begin(), V.end(), ostream_iterator<char>(cout, " "));
cout << endl;
}
|
이것은 컨테이너중에 중복된 요소를 없앰으로 오직 유일한 요소들만을 가지도록 만들어준다.
template <class ForwardIterator> ForwardIterator unique(ForwardIterator first, ForwardIterator last);
|
인자로 주어지는 first 와 last iterator 사이를 순환하며, 중복되는 값을 가지는 원소가 있을경우 처음 원소만 그대로 두고 그뒤에 오는 원소들을 모두 삭제한다.
#include <algorithm>
#include <vector>
int main()
{
vector<int> V;
V.push_back(3);
V.push_back(6);
V.push_back(1);
V.push_back(2);
V.push_back(2);
V.push_back(1);
V.push_back(3);
sort(V.begin(). V.end());
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// 1 1 2 2 3 3 6 이 출력된다.
vector<int>::iterator new_end = unique(V.begin(), V.end());
copy(V.begin(), new_end, ostream_iterator<int>(cout, " "));
cout << endl;
// 1 2 3 6 이 출력된다.
}
|
제목에 현혹되지 말기바란다. 컨테이너에 포함된 원소를 정렬하는 그런 알고리즘들을 뜻하는게 아니다. 여기에서 말하는 Sorting 이란 원소가 미리 정렬되어있는 을 뜻한다. 여기에서 소개될 알고리즘들을 제대로 사용하기 위해서는 각 컨테이너의 원소가 미리 소팅되어 있어야만 제대로된 결과를 얻을수 있기 때문이다.(sort 알고리즘은 제외)
가장 간단하며 가장 자주 사용되는 알고리즘이다.
template <class RandomAccessIterator> void sort(RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class StrictWeakOrdering>
void sort(RandomAccessIterator first, RandomAccessIterator last,
StrictWeakOrdering comp);
|
인자로 주어지는 first 와 last 이터레이터 사이를 순환하면서 소팅한다.
그런데 그 밑에 있는 오버로딩된 또다른 sort 버젼의 경우 comp 가 추가로 들어가는것을 알수 있다. 이것은 함수객체로써, 비교를 위한 함수를 사용하고자 할때 사용된다. 예를들어서 컨테이너에 들어있는 타입이 int 이거나, string 일경우에는 비교연산이 되므로 굳이 comp 함수객체를 필요로 하지 않을것이다. 그러나 char * 같은경우에는 비교연산이 되지 않으므로, 이럴경우에는 별도의 비교연산을 위한 함수객체가 필요하다.
#include <algorithm>
#include <vector>
#include <string>
bool case_eq(char *c1, char *c2)
{
if (strcmp(c1, c2) < 0)
return 1;
else
return 0;
}
int main()
{
vector<int> V;
V.push_back(4);
V.push_back(7);
V.push_back(2);
V.push_back(3);
V.push_back(1);
V.push_back(6);
sort(V.begin(), V.end());
copy(V.begin(), V.end(), ostream_iterator<int>(cout, "\n"));
vector<char *> V2;
V2.push_back("my name");
V2.push_back("hello");
V2.push_back("ma name");
V2.push_back("hael");
// 만약 case_eq 함수객체를 사용하지 않는다면 sort 되지 않을것이다.
sort(V2.begin(), V2.end(), case_eq);
copy(V2.begin(), V2.end(), ostream_iterator<char *>(cout, "\n"));
}
|
위의 예제를 보면 함수객체가 어떻게 사용되는지 알수 있을것이다. 이 함수객체를 약간만 수정하면 소팅을 역으로 할수도 있다. 단지 strcmp(c1, c2) < 0 을 strcmp(c1, c2) > 0 으로만 바꿔주면 된다.
이름에서 알수 있듯이 이것들은 컨테이너에서 원하는 요소와 연속적으로 일치하는 첫번째 위치와 마지막 위치를 알아내기 위해서 사용한다. STL 의 컨테이너를 공부했다면 multimap 이나 multiset 에 같은이름의 함수를 본적이 있을것이다. 이들과 같은 일을한다.
#include <vector>
#include <algorithm>
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(1);
V.push_back(2);
V.push_back(2);
V.push_back(2);
V.push_back(3);
V.push_back(4);
V.push_back(5);
V.push_back(2);
vector<int>::iterator m_start = lower_bound(V.begin(), V.end(), 2);
vector<int>::iterator m_end = upper_bound(V.begin(), V.end(), 2);
copy(m_start, m_end, ostream_iterator<int>(cout, " "));
// 2 2 2 가 출력될것이다.
// 마지막의 2는 연속된 값이 아님으로 무시된다.
}
|
2개의 컨테이너의 원소를 결합한다. 컨테이너의 원소들은 결합되면서 자동적으로 sort 된다. 만약 원소가 비교연산을 지원하지 않는 타입의 원소라면 비교객체 함수를 정의해서 사용하면 된다. 이외에도 함수객체는 알고리즘의 연산을 방식을 변경하기 위한 용도로도 사용된다. 이 알고리즘을 적용하기 위해서는 각 컨테이너의 원소가 미리 정렬되어 있어야 한다.
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
template <class InputIterator1, class InputIterator2, class OutputIterator,
class StrictWeakOrdering>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, StrictWeakOrdering comp);
|
merge 된후의 값은 result 로 출력가능하다.
#include <vector>
#include <algorithm>
int main()
{
vector<int> V1;
vector<int> V2;
V1.push_back(1);
V1.push_back(3);
V1.push_back(5);
V2.push_back(2);
V2.push_back(4);
V2.push_back(5);
V2.push_back(6);
merge(V1.begin(), V1.end(), V2.begin(), V2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
}
|
만약 원소가 비교연산이 불가능한 char * 과 같은 형일경우에는 객체함수 comp 를 정의하면 된다. 정의 방법인 이전에 이미 설명했음으로 그냥 넘어가도록 하겠다.
각각 합집합, 교집합, 차집합을 구현하는 알고리즘이다. 때에 따라서 매우 유용하게 사용할수 있을것이다.
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator set_union(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
template <class InputIterator1, class InputIterator2, class OutputIterator,
class StrictWeakOrdering>
OutputIterator set_union(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result,
StrictWeakOrdering comp);
|
set_union 알고리즘만 출력했는데, 다른것들역시 이름만 다르고 동일하게 사용할수 있다. 비교객체 함수인 comp 를 정의함으로써, 결과값을 sort 할수있다. comp 를 이용해서 알고리즘의 연산방식을 변경시켜줄수 있다.
또한 컨테이너에 입력되어 있는 원소들은 미리 sort 되어 있어야 한다.
#include <algorithm>
#include <vector>
int main()
{
vector<int> V1, V2;
V1.push_back(1);
V1.push_back(2);
V1.push_back(3);
V1.push_back(4);
V1.push_back(7);
V2.push_back(0);
V2.push_back(1);
V2.push_back(7);
V2.push_back(8);
V2.push_back(9);
set_union(V1.begin(), V1.end(), V2.begin(), V2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// 0 1 2 3 4 7 8 9
set_intersection(V1.begin(), V1.end(), V2.begin(), V2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// 1 7 이 출력된다.
set_difference(V1.begin(), V1.end(), V2.begin(), V2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// 2 3 4 이 출력된다.
}
|
이번에는 수치알고리즘에 대해서 알아보도록 하겠다. 이들 수치 알고리즘을 사용하기 위해서는 numeric 를 include 시켜야 한다.
컨테이너의 모든 원소를 더하기 위해서 사용한다.
template <class InputIterator, class T> T accumulate(InputIterator first, InputIterator last, T init);
template <class InputIterator, class T, class BinaryFunction>
T accumulate(InputIterator first, InputIterator last, T init,
BinaryFunction binary_op);
|
첫번째와 두번째 인자는 알고리즘을 적용할 원소의 범위를 지정하기 위한 이터레이터 이며, 세번째 아규먼트는 초기값이다. 또한 다른 많은 알고리즘과 마찬가지로 함수 객체를 이용해서 알고리즘의 연산방식을 변경시킬수도 있다.
#include <numeric>
#include <vector>
int mysum(int a, int b)
{
return a * b;
}
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(2);
V.push_back(3);
V.push_back(4);
V.push_back(5);
V.push_back(6);
V.push_back(7);
cout << accumulate(V.begin(), V.end(), 0) << endl;
// 120
cout << accumulate(V.begin(), V.end(), 1, mysum) << endl;
// 5040
}
|
2번째 accumulate 를 적용시킬때는 mysum 이라는 함수 객체를 이용해서 기존의 '+' 연산을 '*' 연산을 하도록 변경했음을 알수 있다.
partical_sum 은 x 0, x 0 + x 1, x 0 + x 1 + x 3 과 같은 연산을 할때 사용한다.
template <class InputIterator, class OutputIterator>
OutputIterator partial_sum(InputIterator first, InputIterator last,
OutputIterator result);
template <class InputIterator, class OutputIterator, class BinaryOperation>
OutputIterator partial_sum(InputIterator first, InputIterator last,
OutputIterator result, BinaryOperation binary_op);
|
첫번째와 두번째 아규먼트는 컨테이너에서 알고리즘을 적용할 구간의 iterator 값이다. 다른 알고리즘과 마찬가지로 함수객체를 이용해서 알고리즘의 연산을 변경할수 있다.
#include <numeric>
#include <vector>
#include <algorithm>
int mysum(int a, int b)
{
return a * b;
}
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(2);
V.push_back(3);
V.push_back(4);
V.push_back(5);
partial_sum(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
cout << endl;
partial_sum(V.begin(), V.end(), ostream_iterator<int>(cout, " "), mysum);
cout << endl;
}
|
첫번째 partial_sum 은 1, 1+2, 1+2+3, 1+2+3+4 .. 의 연산을 수행할것이다. 반면 두번째 연산은 함수객체에 의해서 1, 1*2, 1*2*3, 1*2*3 .. 의 연산을 수행하게 될것이다.
이것은 x 0, x 1 - x 0, x 2 - x 1.. 과 같이 인접한 두원소의 차를 구한다.
template <class InputIterator, class OutputIterator>
OutputIterator adjacent_difference(InputIterator first, InputIterator last,
OutputIterator result);
template <class InputIterator, class OutputIterator, class BinaryFunction>
OutputIterator adjacent_difference(InputIterator first, InputIterator last,
OutputIterator result,
BinaryFunction binary_op);
|
역시 구간을 정하기 위한 2개의 iterator 가 사용되며, 알고리즘의 연산을 변경하기 위한 함수객체가 사용되기도 한다.
#include <numeric>
#include <vector>
int mysum(int a, int b)
{
return a * b;
}
int main()
{
vector<int> V;
V.push_back(1);
V.push_back(2);
V.push_back(3);
V.push_back(4);
V.push_back(5);
adjacent_difference(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
cout << endl;
adjacent_difference(V.begin(), V.end(), ostream_iterator<int>(cout, " "), mysum);
cout << endl;
}
|
이것은 두개의 컨테이너의 원소들의 내적을 구하고자 할때 사용된다. 즉 A 와 B 컨테이너의 내적을 구한다면 A 0 X B 0 + A 1 X B 1 + ... A n X B n 공식을 따르게 될것이다.
Inner_product is an overloaded name; there are actually two inner_product functions.
template <class InputIterator1, class InputIterator2, class T>
T inner_product(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init);
template <class InputIterator1, class InputIterator2, class T,
class BinaryFunction1, class BinaryFunction2>
T inner_product(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init, BinaryFunction1 binary_op1,
BinaryFunction2 binary_op2);
|
#include <numeric>
#include <vector>
#include <algorithm>
int mysum(int a, int b)
{
return a * b;
}
int main()
{
vector<int> V1;
vector<int> V2;
V1.push_back(1);
V1.push_back(2);
V1.push_back(3);
V1.push_back(4);
V2.push_back(5);
V2.push_back(6);
V2.push_back(7);
V2.push_back(8);
V2.push_back(9);
cout << inner_product(V1.begin(), V1.end(), V2.begin(), 0) << endl;
}
|
위의 결과값은 1*5+2*6+3*7+4*8 = 70 이 될것이다.
|
요사이 일본이라는 나라의 한 우익 신문의 개그 코너를 가지고 열심히 토론을 한다.
개그는 개그 일뿐 무시하고 싶지만 나의 정서나 대한 민국의 모든 국민의 정서가
그 개그 코너를 무시할수가 없다. 그래서 대한 민국은 정말로 갑갑하고 답답하다.
나는 국제 정치니 국내 정치니 아무튼 그쪽에는 큰관심이 없다. 억지로 대통령의 이름 석자나 알정도다. 그래서 대한 민국의 문제는 잘 볼수가 없다.
그러니 국제 정세는 도통 알고 싶지도 않다. 그래서 알려고 열씸히 노력도 안한다.
문제는 국민성에 있다고 생각한다.
가미가제라 불리던 자살 특공대의 인간 미사일이다.
사람이 직접 조정해서 ... 다음은 생각하기도 싫다. 아마도 롤러 코스트의
짜릿함의 수만배쯤 될까. 문제는 이런 자살공격을 지시한 사람들이 지금 일본의 우익 세력이라는 것이다.적어도 그들의 자손이 될것이다.
젊은 청년을 그냥 무기로 만들어버린 사람들이 그때는 개그맨이 아니고 엄청 잔인한 희극 배우들이었을 것이다.
달리는 이런 유인병기들도 무시무시하지만 그보다 더 무서운 건 결정적인 건 저런 무기를 만들라 지시하고, 수많은 젊은이들을 죽음으로 내몬 수뇌부 가운데 정작 자신들이 죽으라 지시한 병사들과 같이 죽음으로써 책임진 인간들은 하나도 없다는 것. 오히려 전쟁 이후 여전히 일본의 지배층으로서 온갖 부귀영화를 다 누리다 죽었고 그자손이 여전히 그일들을 미화 시키고 있다.
한 마디로 죽은 사람만 개죽음한 셈이다.
일본의 국민들은 이런 무기들을 야스쿠니 신사에 전시해 두는 이유를 모르겠다. 나같으면 쪽팔려서라도 도저히 그렇게 못 하겠건만. 아직도 스스로 목숨을 버려 진충보국하는 사무라이의 허상을 부여잡고 싶은 것일까? 하기야 그것도 좋다고 찾아가가는 일본 수상들은 과연 제정신일까 ?
더우기 그런 명령을 받고 천왕 만세를 부르고 떠나는 국민들은 ...
지금 전쟁을 미화하고 있는 저들을 보면서도 침묵하고 있는 일본사회
그 자체에도 문제는 반드시 있을 것이다.
그러니 제국주의 일본 스스로 독도는 자기땅이라고 말하는 것은 아마도 너무나
당연할수 있을것이다. 스스로 자기의 잘못을 반성 없이 오히려 정당화 하고 미화 하려는 그런 나리 일본 잘못된 일이라는 것을 알고 있으면서 자기 자식을 유인 폭탄으로 만드는 부모들이 있는 나라 자기 자식이 폭탄이 되는대로 뭐라고 한마디 정부를 향하여 말조차 하지 못하는 그런 부모가 있는 그나라와 왜 우리나라 대한 민국은 말을 섞어야 하는지 모르겠다.
|
|
최근 등록된 덧글
최근 등록된 트랙백
메모장
이전블로그
이글루링크
이글루 파인더
|
|