일반 파괴자는 스택으로 할당된 객체에 대해서는 소멸을 하지만, 동적으로 할당한 메모리에 대해서는 책임지지 않는 문제점이 있다.
실수로 delete문을 빼먹는 경우
#include<iostream>
using namespace std;
void main()
{
double* rate;
rate = new double;
*rate = 3.1415;
cout << *rate << endl;
// delete rate;
}
8바이트의 메모리 누수가 일어난다.
rate 변수는 동적으로 생성한 실수형 변수의 메모리를 저장하는 포인터 변수이다.
rate 변수의 동적 메모리를 해제하는 delete 수행을 주석처리한다.
rate 변수는 종료 시에 자동으로 메모리가 소멸되지만, 이 변수가 가리키는 주소 메모리는 자동으로 해제되지 않는다. 즉, 메모리 누수(Memory Leak)가 발생한다.
동적으로 할당된 메모리는 이름이 없으므로 포인터를 잃어버리면 참조할 수 없어서 해제가 어렵게 된다.
짧은 코드에서는 delete문을 빼먹는 실수는 하지 않을 것이다.
정상적인 실행흐름이면 new/delete가 짝을 이루어 할당 해제가 수행되지만, 예외 조건에 의해 예외 처리 시에는 catch문 수행 후 delete는 수행하지 못하게 된다.
지역 객체가 가리키는 메모리까지 해제되는 것은 아니기 때문에 메모리 누수(Memory Leak)가 발생한다.
이러한 메모리 누수는 양이 많지 않아 당장은 별 문제가 되지 않지만, 오랫동안 실행되는 프로그램은 시스템 자원을 갉아먹기 때문에 나중에는 심각한 문제가 될 수 있다.
이러한 문제를 해결하기 위해 만들어진 것이 바로 auto_ptr이다.
template<typename T> class auto_ptr
#include<iostream>
#include<memory>
using namespace std;
void main()
{
/*double* rate;
rate = new double;
*rate = 3.1415;
cout << *rate << endl;
delete rate;*/
auto_ptr<double> rate(new double);
*rate = 3.1415;
cout << *rate << endl;
}
#include<iostream>
#include<memory>
using namespace std;
template <class T>
class smartPointer
{
private:
T* p;
public:
smartPointer(T* sp)
{
p = sp;
};
~smartPointer()
{
delete p;
};
T operator*() const
{
return *p;
}
T* operator->() const
{
return p;
}
};
void main()
{
smartPointer<string> pStr(new string("test"));
cout << *pStr << endl; // pStr.operator*()
cout << pStr->size() << endl;
}
출력결과
test
4
#ifdef 매크로명
코드
#endif
#include<iostream>
#include<list>
#include<vector>
using namespace std;
#define LIST // 주석처리하면 vector가 실행
void main()
{
int i;
#ifdef LIST
list<int> lst;
list<int>::iterator it;
#else
vector<int>lst;
vector<int>::iterator it;
#endif
for (i = 0; i < 5; i++)
lst.push_back(i);
for (it = lst.begin(); it != lst.end(); it++, i++)
cout << i << "번째=" << *it << endl;
}
소스 코드의 상단에서 심심치 않게 볼 수 있는 매크로이다.
#pragma 지시자는 컴파일러 지시자로써 플랫폼별로 다른 기능들에 대한 지시사항을 컴파일러에게 전달하기 위한 기능을 가지고 있다.
#pragma once를 헤더파일의 선두에 써두면 컴파일러는 딱 한 번만 헤더 파일을 포함한다.
같은 헤더파일을 일부러 두 번 포함하지는 않겠지만 헤더 파일끼리 중첩하다보면 문제가 발생할 수 있다.
조건부 컴파일 지시자로 한 번만 포함되도록 하는 것과 동일한 효과이다.
char *pc = 0;
char& rc = *pc;
string& rs; // 초기화 에러
string s("abc");
string& rs = s; // 초기화된 변수를 참조하므로 문제 없음
void prontDouble(const double& rd)
{
cout << rd;
}
void printDouble(const double *pd)
{
if(pd)
{
cout << *pd;
}
}
포인터를 써야 하는 경우
가리킬 객체의 주소가 없을 때 (널포인터)
하나의 변수를 가지고 여러 개의 객체를 바꾸어 참조해야 하는 경우
참조자를 써야 하는 경우
참조할 포인터가 반드시 존재할 것임을 알고 있을 때
참조할 대상 객체를 바꿀 필요가 없을 때
#include<iostream>
#include<crtdbg.h>
using namespace std;
void main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
int* nData = new int;
}
출력결과
Detected memory leaks!
Dumping objects ->
{74} normal block at 0x000001C9D8553650, 4 bytes long.
Data: < > CD CD CD CD
Object dump complete.
'[14400] AdvancedCplus.exe' 프로그램이 종료되었습니다(코드: 0 (0x0)).
74 : 74번째 메모리가 생성되는 부분에서 에러가 남.
_CrtSetBreakAlloc(74);
문제가 되는 번호를 넣어 문제가 되는 곳을 파악할 수 있다.