c++STL vector<> 에 추가할때 클래스 소멸질문입니다.

hanty11의 이미지

실험(?)을 위해 Integer클래스를 만들었습니다.

class Integer {
	int n;
public :
	Integer(int _n = 0): n(_n){}
	~Integer() {
		cout << "del : " << n << endl;
	}
	operator int () const{
		return n;
	}
};

소멸할때마다 n을 외치고 죽습니다.

그리고 vector에 5까지 추가해서 출력해봤더니

void main(){
        vector<Integer> v;
 
	cout << "insert 1" << endl;
	v.push_back(Integer(1));
 
	cout << "insert 2" << endl;
	v.push_back(Integer(2)); 
 
	cout << "insert 3" << endl;
	v.push_back(Integer(3));
 
	cout << "insert 4" << endl;
	v.push_back(Integer(4));
 
	cout << "insert 5" << endl;
	v.push_back(Integer(5));
	cout << endl << "======================" << endl;
}

결과는 이렇게 나왔습니다.

insert 1
del : 1
insert 2
del : 1
del : 2
insert 3
del : 1
del : 2
del : 3
insert 4
del : 1
del : 2
del : 3
del : 4
insert 5
del : 1
del : 2
del : 3
del : 4
del : 5

======================
del : 1
del : 2
del : 3
del : 4
del : 5
계속하려면 아무 키나 누르십시오 . . .

질문은 왜 추가할때마다 vector에 들어있는 원소들이 한번씩 차례로 다 소멸되는건가요?

kukyakya의 이미지

Integer(1)로 생성된 임시 객체가 백터 안으로 push된 후에 해당 임시 객체가 소멸됩니다.

vector에 push할때 할당해둔 메모리 공간이 부족할 경우 더 큰 공간을 할당한 후 기존에 저장된 원소들을 새로 할당한 공간에 복사하고 기존의 메모리 공간에 존재하던 원소들을 소멸시킨 후 기존의 메모리 공간을 해제합니다.

보통은 2*n + 1같이 점점 더 큰 공간을 할당하는데 어떤 구현체를 사용중이신지 몰라도 해당 구현체가 기존의 공간보다 원소 1개만큼만 더 큰 공간을 매번 할당하나보네요.

예상하자면 다음과 같습니다.

insert 1
del : 1 (임시 객체 소멸)
insert 2
del : 1 (재할당되면서 기존 객체 소멸)
del : 2 (임시 객체 소멸)
insert 3
del : 1 (재할당되면서 기존 객체 소멸)
del : 2 (재할당되면서 기존 객체 소멸)
del : 3 (임시 객체 소멸)
insert 4
del : 1 (재할당되면서 기존 객체 소멸)
del : 2 (재할당되면서 기존 객체 소멸)
del : 3 (재할당되면서 기존 객체 소멸)
del : 4 (임시 객체 소멸)
insert 5
del : 1 (재할당되면서 기존 객체 소멸)
del : 2 (재할당되면서 기존 객체 소멸)
del : 3 (재할당되면서 기존 객체 소멸)
del : 4 (재할당되면서 기존 객체 소멸)
del : 5 (임시 객체 소멸)

======================
del : 1 (vector가 소멸되면서 내부 원소 소멸)
del : 2 (vector가 소멸되면서 내부 원소 소멸)
del : 3 (vector가 소멸되면서 내부 원소 소멸)
del : 4 (vector가 소멸되면서 내부 원소 소멸)
del : 5 (vector가 소멸되면서 내부 원소 소멸)

hanty11의 이미지

vector가 용량을 키울때 기존객체를 복사하느라그런거였군요
알려주셔서 감사합니다!!

HDNua의 이미지

+1

저는 이렇게 생각했습니다.

익명 사용자의 이미지

두 가지 이유가 있습니다.

1. push_back에 넘겨주는 temporary의 소멸.

예컨대 v.push_back(Integer(3));를 호출했을 때, temporary인 Integer 객체가 생성자 인수 3을 받아서 생성됩니다.
temporary는 rvalue인데, push_back의 인수는 const reference이죠.
따라서 temporary가 const reference에 bound될 때의 규칙에 따르게 됩니다.

이 규칙에 대한 얘기는 https://kldp.org/node/154710 에서 제가 한 번 길게 설명했었는데 관심 있으시면 가서 읽어보시고요.
여기서 필요한 부분은 "function call 과정에서 reference parameter에 bound된 temporary는 함수 호출을 포함하는 full expression의 completion까지 살아있게 된다." 입니다.

push_back은 매개변수로 넘어온 객체를, vector가 미리 할당해 놓은 공간에 복사하는데요.
그러고 나면 원본이었던 temporary는 push_back 함수 호출을 포함하는 full expression의 completion까지만 살아 있다가 소멸하게 됩니다.
full expression이니 뭐니 하는 자세한 설명은 필요 없으니 생략하고요. 짧게 줄이면, 위의 예에서는 v.push_back(Integer(3));의 실행이 끝난 뒤에, Integer(3)로 인해 생성된 temporary가 소멸하는 겁니다.

따라서 v.push_back(Integer(3)); 뒤에는 반드시 Integer(3)의 소멸자가 뒤따르는 겁니다.

2. vector의 reallocation에 의한 앞선 객체의 소멸

근데 그러면 Integer(1), Integer(2)의 소멸자는 왜 호출된 걸까요?
이는 vector의 동작 원리에 의한 것입니다.

vector는 반드시 자신이 포함하는 객체들을 연속된 메모리 영역에 배치해야 합니다.
하지만 연속된 메모리 영역은 무한히 확장해갈 수 없지요. 그래서 처음에는 작은 크기만을 할당해 놓습니다.
그러다가 할당된 영역을 모두 쓰고 나면, 더 큰 메모리 영역을 할당하고, 모든 객체를 복사한 뒤, 이전에 쓰던 메모리 영역을 할당 해제합니다.
그 과정에서 복사 생성자(이전 영역의 객체로부터 새 영역으로 복사 생성)와 소멸자(이전 영역에 있던 객체를 소멸시킴)가 필요하지요.
(C++11에서는 복사 생성이 아니라 이동 생성될 수도 있습니다. 그래도 소멸자는 호출됩니다.)

vector<..> v에 대해서, v에 현재 할당된 메모리 영역에 객체가 최대 몇 개까지 들어갈 수 있는지를 반환하는 멤버 함수는 capacity입니다. 반면 실제로 들어가 있는 객체의 수는 size 멤버 함수를 호출하면 알 수 있습니다.

그러니 size = capacity일 때 (즉 vector가 꽉 찼을 때) push_back을 호출하면, vector에서 reallocation이 일어나면서 이전 메모리 영역에 있었던 모든 객체에 대해 소멸자가 호출되는 것이죠.

한 가지 더 짚고 넘어가죠. reallocation이 일어났을 때 vector가 할당하는 새 메모리 영역의 크기가 이전에 비해 얼마나 커지느냐 하는 건 라이브러리 개발자가 정하기 나름입니다. 보통 2배로 커집니다.

아래 코드를 보세요.

#include <iostream>
#include <vector>
using namespace std;
 
class Integer {
        int n;
        public :
        Integer(int _n = 0): n(_n){}
        ~Integer() {
                cout << "del : " << n << endl;
        }
        operator int () const{
                return n;
        }
};
 
int main(void){
        vector<Integer> v;
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 1" << endl;
        v.push_back(Integer(1));
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 2" << endl;
        v.push_back(Integer(2));
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 3" << endl;
        v.push_back(Integer(3));
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 4" << endl;
        v.push_back(Integer(4));
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 5" << endl;
        v.push_back(Integer(5));
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << endl << "======================" << endl;
        return 0;
}

실행 결과는 아래와 같습니다.

v.size() = 0, v.capacity() = 0
insert 1
del : 1
v.size() = 1, v.capacity() = 1
insert 2
del : 1
del : 2
v.size() = 2, v.capacity() = 2
insert 3
del : 1
del : 2
del : 3
v.size() = 3, v.capacity() = 4
insert 4
del : 4
v.size() = 4, v.capacity() = 4
insert 5
del : 1
del : 2
del : 3
del : 4
del : 5
v.size() = 5, v.capacity() = 8
 
======================
del : 1
del : 2
del : 3
del : 4
del : 5

push_back으로 삽입하는 temporary의 소멸은 항상 관찰되는 반면, 이전 객체들의 소멸은 size = capacity일 때 (즉 vector가 꽉 찼을 때) push_back을 호출했을 때만 발생하죠? 또한 그 이후에 capacity가 어떻게든 이전 값보다 커져 있다는 것도 주목할 만합니다. 구체적으로 얼마나 늘었는지는 구현환경마다 다를 수 있어요. 저는 Ubuntu 14.04, x86_64, g++ 4.8.2로 돌렸습니다.

3. 그래서, 이런 소멸자 호출을 없앨 수 있을까요?

1) push_back에 넘기는 temporary의 소멸은 피할 수 없었습니다. C++03까지는 말이죠.
C++11부터는 다른 방법이 하나 생겼는데요. 차근차근 설명 드리죠.

우선 C++11 컴파일러의 눈에는 위 코드가 C++03 컴파일러가 봤을 때와 좀 다르게 보입니다.
C++11의 관점에서 볼 때, push_back에 넘겨지는 temporary는 rvalue이고, 분명히 push_back에 넘겨지는 용도로밖에 안 쓰일 거란 말이죠.
그래서 C++11의 push_back는 temporary를 vector로 "복사"하는 게 아니라 "이동"시킵니다. 대부분의 상황에서, 객체를 이동시키는 건 복사하는 것보다 더 저렴하기 마련이죠.

하지만 "이동"될 때에도 원본 temporary에는 여전히 소멸자가 호출되어야 합니다. 하지만 그것조차도 막을 수 있는 방법이 C++11에는 있는데, 그게 바로 Perfect forward(완벽 전달)을 기반으로 한 새 멤버 함수, emplace_back이죠.

요점은 이렇습니다. 괜히 temporary를 만들어서 push_back를 통해 vector 내부로 이동시키지 말고, 그냥 생성자 인수들을 건네 주면 vector가 스스로 필요한 위치에 객체를 생성하겠다는 거에요.

직접 한 번 보세요. 컴파일하려면 C++11을 지원하는 컴파일러에 C++11 활성화 옵션을 줘야 합니다.

#include <iostream>
#include <vector>
using namespace std;
 
class Integer {
        int n;
        public :
        Integer(int _n = 0): n(_n){}
        ~Integer() {
                cout << "del : " << n << endl;
        }
        operator int () const{
                return n;
        }
};
 
int main(void){
        vector<Integer> v;
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 1" << endl;
        v.emplace_back(1); // Integer 객체를 만든 뒤 넘겨주는 대신, 그 생성자 인수를 넘겨서 vector가 직접 생성하게 합니다.
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 2" << endl;
        v.emplace_back(2);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 3" << endl;
        v.emplace_back(3);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 4" << endl;
        v.emplace_back(4);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 5" << endl;
        v.emplace_back(5);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << endl << "======================" << endl;
        return 0;
}

실행 결과는 아래와 같습니다.

v.size() = 0, v.capacity() = 0
insert 1
v.size() = 1, v.capacity() = 1
insert 2
del : 1
v.size() = 2, v.capacity() = 2
insert 3
del : 1
del : 2
v.size() = 3, v.capacity() = 4
insert 4
v.size() = 4, v.capacity() = 4
insert 5
del : 1
del : 2
del : 3
del : 4
v.size() = 5, v.capacity() = 8
 
======================
del : 1
del : 2
del : 3
del : 4
del : 5

짠. 이제 Integer(3)를 넣을 때 3에 대한 소멸자가 호출되지 않습니다. 1, 2, 4, 5에 대해서도 마찬가지고요.
vector가 생성자 인수(int)를 받아 필요한 자리에 곧바로 생성하니까요.

2) 근데 여전히 vector의 reallocation에 의한 소멸자 호출은 일어나는군요.

vector reallocation은 가변 길이의 연속된 메모리 영역을 쓰는 데 있어서 거의 어쩔 수 없는 비용입니다.
하지만 꽤 비싼 비용이죠. 사실 소멸자 호출보다도, 한 번 일어날 때마다 vector 안의 모든 객체를 복사(C++11에서는 이동일 수도 있음)해야 하는 부담이 훨씬 더 커요.

그러니, 막을 수 있다면 막는 것이 좋습니다.

vector의 reallocation을 예방하는 방법은, vector의 내부 메모리 영역을 일찌감치 충분히 크게 잡아놓는 겁니다.
때로는 vector 안에 들어갈 객체의 최대 수를 어림잡을 수 있잖아요. 그 크기로 vector를 미리 예약(reserve)하면 됩니다. 마침 멤버 함수 이름도 그대로 reserve입니다.

아래 코드를 보세요. 여전히 C++11 컴파일이 필요합니다.
reserve는 C++03때도 있었던 멤버 함수이지만, 이 예제도 emplace_back을 쓰거든요.

#include <iostream>
#include <vector>
using namespace std;
 
class Integer {
        int n;
        public :
        Integer(int _n = 0): n(_n){}
        ~Integer() {
                cout << "del : " << n << endl;
        }
        operator int () const{
                return n;
        }
};
 
int main(void){
        vector<Integer> v;
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        v.reserve(5); // 총 5개의 객체를 넣을 겁니다. 미리 예약해 놓읍시다.
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 1" << endl;
        v.emplace_back(1); // Integer 객체를 만든 뒤 넘겨주는 대신, 그 생성자 인수를 넘겨서 vector가 직접 생성하게 합니다.
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 2" << endl;
        v.emplace_back(2);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 3" << endl;
        v.emplace_back(3);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 4" << endl;
        v.emplace_back(4);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 5" << endl;
        v.emplace_back(5);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << endl << "======================" << endl;
        return 0;
}

vector에 객체를 다섯 개까지만 넣을 것을 미리 알고 있었으므로, 미리 크기를 5로 예약했습니다.
실행 결과는 아래와 같습니다.

v.size() = 0, v.capacity() = 0
v.size() = 0, v.capacity() = 5
insert 1
v.size() = 1, v.capacity() = 5
insert 2
v.size() = 2, v.capacity() = 5
insert 3
v.size() = 3, v.capacity() = 5
insert 4
v.size() = 4, v.capacity() = 5
insert 5
v.size() = 5, v.capacity() = 5
 
======================
del : 1
del : 2
del : 3
del : 4
del : 5

reserve 멤버 함수의 호출로 인해 vector 안의 객체 숫자는 변하지 않았지만(아무 것도 안 넣었으니까요) 용량은 5로 미리 예약된 것을 확인할 수 있습니다. 이후 reallocation은 전혀 일어나지 않았고, 소멸자도 (vector 자체가 소멸되기 전까지) 한 번도 안 불렸습니다. 해결 됐죠. 그렇죠?

3) 덤.
reservevector를 효율적으로 사용하는 데 매우 중요한 멤버 함수입니다. 알아 두세요.
하지만 vector를 필요 이상으로 크게 예약해 두면 그만큼의 메모리가 묶여 있게 됩니다. 필요가 없어지면 풀어 줘야죠.

하지만 vector의 용량을 줄이기 위해 reserve에 현재 용량보다 작은 값을 넘겨서 호출해 봤자 아무 의미도 없습니다. reservevector의 용량을 키우는 역할만 하거든요.

vector의 용량을 줄이는 방법은 C++03까지는 간단한 방법이 없어서, swap을 이용한 약간 복잡한 편법을 동원해야만 했죠. C++11부터는 간단한 방법이 생겼습니다. shrink_to_fit이라는 멤버 함수입니다.

hanty11의 이미지

사실 학생인데 성능부분에대한 관심이 많아서 좀 공부하고있다가
소멸자가 계속호출되는걸 알게되고 궁금했던거였거든요
자세한설명 정말 감사드립니다.
좋은 기법들 배워갑니다!

ghkdtjdgus33의 이미지

이동 생성자에 대해 공부하고 있었는데 다른 방법도 알게 되었네요
감사합니다 !

익명 사용자의 이미지

gcc(libstdc++) 쪽에서 테스트해보면 2 4 8 16 32 ... 단위로 reserve 하는 걸로 보입니다.
vs2015 에서 테스트해보면 글쓴 분과 같은 결과가 나옵니다.

vs2015 에서는 더 크게 테스트해보면 5 7 10 14 20 29 43 64 ... (!?) 단위로 reserve 하네요.
늘리는 부분 소스는 대략 아래와 같습니다.

size_type _Grow_to(size_type _Count) const
{ // grow by 50% or at least to _Count
size_type _Capacity = capacity();

_Capacity = max_size() - _Capacity / 2 < _Capacity
? 0 : _Capacity + _Capacity / 2; // try to grow by 50%
if (_Capacity < _Count)
_Capacity = _Count;
return (_Capacity);
}

희한하네요.

twinwings의 이미지

g++ -std=c++11 로 컴파일 했는데 저도 1개씩 증가시키네요.

저도 2배씩 메모리 할당 받는 것으로 알고 있었는데데

hanty11의 이미지

VC2015기준으론
1 - 2 - 3 - 4 - 6 - 9 - 13 - 19 - 28 -42
이러네요...

twinwings의 이미지

덧붙이자면,

vector의 메모리를 미리 잡아두는 메소드가 존재합니다.

그리고 생성자는 존재하는데 복사 생성자가 없네요.

STL 컨테이너들은 기본적으로 Call by Value로 동작하기 때문에 복사 생성자가 꼭 필요합니다.

이 경우에는 동적할당된 멤버변수가없어서 문제 없지만

그렇지 않는 경우 심각한 메모리 참조 오류가 발생합니다.

twinwings의 이미지

C++11을 오랫동안 사용안했더니 emplace_back 메소드를 완전 잊고 있었네요.

작성한 코드는 너무 옛날 방식이라 삭제합니다. 익명분 코드 보시면 도움이 많이 될 듯 하네요.

hanty11의 이미지

STL쓸때 복사생성자없을때 메모리문제는 생각못했네요
팁주셔서 감사합니다.

익명 사용자의 이미지

STL 컨테이너가 Call by Value로 동작한다는 말씀은 무슨 의미인지 잘 이해되지 않는군요.
의도하셨던 바는 아마도 C++03까지의 STL 컨테이너들이 객체의 복사 기능에 의존하여 동작한다는 말씀이시겠죠?

C++11부터는 사실 객체가 복사 가능하지 않아도 이동 가능하기만 하면(즉 이동 생성/대입이 가능하면) 괜찮은 경우가 많습니다.
사실 STL이 객체를 복사하려던 이유도, 정말 복제본을 만들기 위해서라기보다는 이동을 위해서였으니까요.

그리고 복사 생성자와 복사 대입 연산자는 C++03의 특수 멤버 함수에 속하며, 직접 정의하지 않아도 컴파일러가 암시적으로 적절히 trivial한 꼴으로 만들어 줍니다. 사실 C++03에서는 이들 함수를 정의하지 않으면서 컴파일러가 대신 작성해주는 걸 금지하는 편이 좀 더 까다롭습니다.

그러나 소위 3의 법칙이라고 하여, 객체의 소멸자/복사 생성자/복사 대입 연산자 중 하나라도 직접 작성하였다면(그럴 필요가 있었다면) 나머지 둘도 직접 작성하는 것이 바람직합니다. 논리적 근거가 있는데, 자세한 내용은 생략하도록 하죠.
이 질문 예제의 경우엔 애초에 짧은 예제이기도 하고, 소멸자를 직접 정의한 이유도 다른 게 아니라 그냥 로그를 남기기 위한 목적이므로, 컴파일러가 복사 생성자를 작성하도록 하는 걸 그다지 거리낄 필요는 없을 것 같다고 생각합니다.

3의 법칙은 C++11에서도 여전히 유의미한 지침이며, C++11 컴파일러는 여전히 사용자 정의 복사 생성자가 없을 때 자동으로 작성해 주기는 하지만, "사용자가 정의한 소멸자/복사 대입 연산자가 있을 때 컴파일러가 복사 생성자를 암시적으로 대신 작성해 주는 것"은 비권장 기능으로 분류되어 있습니다. 즉 가급적이면 복사 생성자와 복사 대입 연산자도 같이 정의해 주시는 편이 좋습니다.

사실 C++11에선 명시적으로 컴파일러한테 대신 작성해 달라고 하거나, 컴파일러의 작성을 금지해버리는 방법도 있습니다.
마찬가지로 C++11 컴파일이 필요한 코드 제시해 드리겠습니다.

#include <iostream>
#include <vector>
using namespace std;
 
class Integer {
        int n;
        public :
        Integer(int _n = 0): n(_n){}
        Integer(const Integer &) = delete;              // 복사 생성자 삭제
        Integer &operator=(const Integer &) = delete;   // 복사 대입 연산자 삭제
        Integer(Integer &&) = default;                  // 이동 생성자는 컴파일러에게 맡김
        Integer &operator=(Integer &&) = default;       // 이동 대입 연산자도 컴파일러에게
        ~Integer() {
                cout << "del : " << n << endl;
        }
        operator int () const{
                return n;
        }
};
 
int main(void){
        vector<Integer> v;
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        v.reserve(5); // 총 5개의 객체를 넣을 겁니다. 미리 예약해 놓읍시다.
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 1" << endl;
        v.emplace_back(1); // Integer 객체를 만든 뒤 넘겨주는 대신, 그 생성자 인수를 넘겨서 vector가 직접 생성하게 합니다.
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 2" << endl;
        v.emplace_back(2);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 3" << endl;
        v.emplace_back(3);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 4" << endl;
        v.emplace_back(4);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << "insert 5" << endl;
        v.emplace_back(5);
 
        cout << "v.size() = " << v.size() << ", v.capacity() = " << v.capacity() << endl;
 
        cout << endl << "======================" << endl;
        return 0;
}
gilgil의 이미지

좋은 정보 감사합니다.

gilgil의 이미지

user define class를 가지고 테스트를 하는 방법도 있지만,
malloc, free, new, delete 등을 재정의해서
primitive type(int)을 가지고도 메모리의 작동을 확인할 수 있습니다.
참고해 보시기 바랍니다.
http://www.gilgil.net/827397

익명 사용자의 이미지

잘만활용하면 엄청편할것같습니다 감사합니다

gilgil의 이미지

굳이 qt 사용할 필요도 없구요, 그냥 src/mem 폴더에 있는 소스 코드 그냥 붙여 넣기만 하셔도 됩니다. ^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.