좋 은 C++프로 그래 밍 습관 을 기 르 는 메모리 관리 에 대한 상세 한 설명
이 시 리 즈 는 과학 보급 도서 로 자리 잡 았 지만 초보 자 들 이 참고 할 수 있 을 뿐만 아니 라 늙 은 새들 의 반성 과 공감 을 불 러 일 으 킬 수 있다 고 믿 습 니 다.여러분 의 소중 한 의견 과 피드백 을 환영 합 니 다^ ^
이 장의 주요 내용 을 시작 하기 전에 본 자 리 는 먼저 작은 지면 으로 좋 은 업무 습관 인 축적,추출 과 수정 을 논술 한다.일과 학습 과정 에서 배 운 지식 을 효과 적 인 방식 으로 쌓 아 자신의 지식 고 를 형성 하고 지식 양 이 확대 되면 서 양 적 변화 에서 질 적 변화 로 향상 시 킬 수 있다.또한 지식 을 계속 추출 해 야 한다.자신의 지식 면 이 확대 되 고 수준 이 향상 되면 서 기 존의 지식 창고 에 단편 적 이 고 한계 가 있 으 며 서 툴 고 심지어 잘못 이 존재 한 다 는 것 을 알 게 될 것 이다.이 럴 때 는 지식 고 를 최적화 정리 하 는 태도 와 끈기 가 필요 하 다.
아마도 이상 의 사람들 은 과거 에 실시 하고 그 이 치 를 알 고 있 었 을 것 이다.그러나 자신 은 자신 에 게 여러 가지 당당 한 핑 계 를 대고 시간 을 들 이지 않 았 다.이렇게 말 하면 기술 의 길 은 걷 기 어렵다.이 업 계 는 두 가지 기본 적 인 요구 가 있다.1.소프트웨어 개발 업무 자체 에 큰 관심 이 있다.2.외로움 을 참 을 수 있다.둘 중 하나 가 없어 서 는 안 됩 니 다.그렇지 않 으 면 젊 을 때 일찍 전업 하 세 요.소프트웨어 업계 의 판매,제품 또는 관리 로 전환 하 셔 도 됩 니 다.어쨌든 개발 은 하지 마 세 요^ ^
--------------------------------------------------------------------------------
질문
C/C++의 메모리 관리 하면 대부분의 사람들의 머 릿 속 에 new/delete/malloc/free 등 몇 개의 무 서운 단어 가 떠 오 르 죠?확실히 C/C++의 수 동 메모리 관 리 는 다른 언어 와 구별 되 는 큰 특징 이 고 다른 언어 에서 C/C++로 전환 하려 는 사람들 앞 에 장벽 이 서 있 는 것 과 같다.이 를 통 해'C++인기 저하'와'쓰레기 수 거 메커니즘 을 도입 해 야 하 는가'등 관련 이 슈 에 대한 각종 포럼 의 갑론을박 도 일 고 있다.이 자 리 는 이러한 논쟁 을 무시 해 왔 다.사실 이 자 리 는 C++의 발전 과 운명 에 관심 이 없 는 것 이 아니 라 반대로 이 자 리 는 매우 관심 을 가지 고 있다.현재 의 안목 으로 볼 때 C++몸 에 얼마나 많은 경상 이 있 든 지 간 에 C++위원회 의 나리 들 과 C++컴 파일 러 업 체 의 사내 들 이 어떻게 꼬리 를 잡 는 지 알 수 있다.결국 가장 사랑 하 는 것 은 가장 사랑 하 는 것 이 고,불완전한 아름다움 도 아름 답 기 때문에 설명 하지 않 는 다.이 자리 가 이런 논쟁 에 관심 이 없 는 이 유 는 한 가지 언어 가 인생 과 같 고 생명 주기 가 있 으 며 몰락 은 빠 른 문제 일 뿐 낡은 사물 은 항상 새로운 사물 로 대체 되 기 때문이다.이것 은 객관 적 인 규칙 이 불가피 하 다.진시황 도 결국 불로장생 의 구슬 을 찾 지 못 했 지 않 습 니까?빛 을 발 하고 열 을 낸 적 이 있다 면 가치 가 있 을 때 대중 에 게 사 용 될 수 있다 는 것 만으로 도 여한 이 없다.이 자 리 는 어떤 언어 도 배척 하지 않 는 반면 새로운 언어의 탄생 에 관심 이 많다 는 태 도 를 밝 혀 야 한다.그들의 특징 을 알 아 보고 어떤 문 제 를 해결 하 는 데 도움 이 되 는 지 살 펴 보 겠 다.요 몇 년 동안 업무 수요 로 인해 본 자 리 는 자바 와 일부 동적 언어(그들 은 확실히 많은 문 제 를 해결 할 수 있다)를 가장 많이 사 용 했 지만 C/C++는 더 이상 사용 하지 않 았 다.
응,멀리 갔 어.우리 본론 으로 돌아 가자.C/C++의 메모리 관 리 는 보기 만 해도 무 서운 것 같 습 니 다.화면 가득 한 new/delete/malloc/free,OutPut 창의 끝 없 는 Memory Leak 경고,프로그램의 기괴 한 0x 0000004 지침 이상,마치 그 해 우리 가 함께 울 었 던 날 로 돌아 가 는 것 같 습 니 다.당신 은 잡 을 수 있 습 니까?사실 현실 은 네가 생각 하 는 것 만큼 그렇게 나 쁘 지 않다.조금 만 신경 쓰 면 돼,맞 아!조금 뿐 입 니 다.C++클래스 로 메모리 에 접근 하면 대부분의 고민 을 해결 하고 평생 이익 을 얻 을 수 있 습 니 다.윈도 프로그램 을 예 로 들 면 다음 과 같은 몇 가지 메모리 관리 방식 이 있 습 니 다.
•가상 메모리(Virtual Memory)•기본 더미 와 개인 더미(Process Heap&Private Heap)•메모리 맵 파일(File Mapping)•프로 세 스 스 스 택(Heap,사실은 malloc()또는 기본 new 연산 자 를 사용 하여 Process Heap 에서 고 기 를 조금씩 자 르 는 것 입 니 다^^)•스 택(Stack,메모 리 는 호출 자 나 호출 자가 자동 으로 관리 합 니 다)오늘 우리 의 주 제 는 패키지 입 니 다.모든 메모리 모델 의 개념 과 API 사용 방식 에 대해 서 는 말 하지 않 겠 습 니 다.구 글 은 바로 알 고 있 습 니 다.사실 C++로 상기 4 가지 메모리 에 접근 하 는 원 리 는 차이 가 많 지 않 습 니 다.구조 함수 나 다른 조작 함수 에서 메모 리 를 분배 한 다음 에 분석 함수 에서 메모리 가 정확하게 방출 되도록 하 는 것 입 니 다.가상 메모리,기본 더미 와 개인 더미 의 조작 방식 이 비슷 합 니 다.여 기 는 일일이 보 여 주지 않 습 니 다.관심 있 는 친 구 는 며칠 전에 발표 한 아무 도 관심 을 갖 지 않 는 글 을 참고 할 수 있 습 니 다.메모리 맵 파일 의 패 키 징 에 대해 서도 조금 만 소개 하 겠 습 니 다.주파수 가 가장 높 은 malloc()와 new 패 키 징 을 사용 하 는 것 을 주로 토론 합 니 다.
--------------------------------------------------------------------------------
메모리 맵 파일
다음 코드 는 File Mapping 핸들 과 File Mapping 에서 비 친 메모 리 를 각각 CFileMapping 과 CShareMemory 에 봉 하여 CShareMemory 를 직접 사용 하면 File Mapping 과 File Mapping 을 비 추 는 메모 리 를 만 들 수 있 습 니 다.
class CFileMapping
{
public:
CFileMapping(
LPCTSTR lpszName,
DWORD dwMaximumSizeLow,
DWORD dwMaximumSizeHigh = 0,
HANDLE hFile = INVALID_HANDLE_VALUE,
DWORD flProtect = PAGE_READWRITE,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes = NULL
)
{
m_hMap = ::CreateFileMapping (
hFile,
lpFileMappingAttributes,
flProtect,
dwMaximumSizeHigh,
dwMaximumSizeLow,
lpszName
);
ASSERT(IsValid());
}
~CFileMapping()
{
if(IsValid())
VERIFY(::CloseHandle(m_hMap));
}
LPVOID ViewMap (
DWORD dwNumberOfBytesToMap,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh = 0,
DWORD dwDesiredAccess = FILE_MAP_ALL_ACCESS
)
{
return ::MapViewOfFile (
m_hMap,
dwDesiredAccess,
dwFileOffsetHigh,
dwFileOffsetLow,
dwNumberOfBytesToMap
);
}
BOOL UnViewMap(LPCVOID lpBaseAddress)
{
return ::UnmapViewOfFile(lpBaseAddress);
}
operator HANDLE () {return m_hMap;}
BOOL IsValid () {return m_hMap != NULL;}
private:
HANDLE m_hMap;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CFileMapping)
};
class CShareMemory
{
public:
CShareMemory(DWORD dwSize, LPCTSTR lpszName = NULL)
: m_fm(lpszName, dwSize)
{
ASSERT(dwSize > 0);
}
~CShareMemory()
{
for(set<ULONG_PTR>::const_iterator it = m_set.begin(); it != m_set.end(); ++it)
{
LPVOID pV = (LPVOID)*it;
ASSERT(pV);
m_fm.UnViewMap(pV);
}
m_set.clear();
}
LPVOID Alloc(DWORD dwNumberOfBytesToMap, DWORD dwFileOffsetLow)
{
LPVOID pV = m_fm.ViewMap(dwNumberOfBytesToMap, dwFileOffsetLow);
if(pV) m_set.insert((ULONG_PTR)pV);
ASSERT(pV);
return pV;
}
BOOL Free(LPCVOID lpBaseAddress)
{
ASSERT(lpBaseAddress);
set<ULONG_PTR>::iterator it = m_set.find((ULONG_PTR)lpBaseAddress);
if(it != m_set.end())
m_set.erase(it);
return m_fm.UnViewMap(lpBaseAddress);
}
private:
CFileMapping m_fm;
set<ULONG_PTR> m_set;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CShareMemory)
};
세심 한 친 구 는 사실 이렇게 포장 하 는 것 이 단점 이라는 것 을 알 게 될 것 이다.우선,CShare Memory 는 메모리 공유 만 할 수 있 고 실제 파일 에 투사 할 수 없다(hFile 은 영원히 INVALID 이다.HANDLE_VALUE);둘째,CShareMemory 의 Alloc()와 Free()방법 을 한층 더 봉인 할 수 있 고,봉인 류 의 석조 함 수 를 이용 하여 Free()를 자동 으로 호출 하면'set
malloc()시리즈 함수
많은 사람들 이 C++에서 malloc()대신 new 연산 자 를 사용 하 라 고 건의 합 니 다.new 형식 이 안전 하기 때문에 구조 함수 와 석조 함수 등 을 자동 으로 호출 합 니 다.이 자리 에 대해 약간의 이의 가 있 습 니 다.어떤 상황 에서 malloc()는 new 보다 더 좋 습 니 다.효율 적 인 측면 에서 우 리 는 따 지지 않 아 도 됩 니 다.이러한 데 이 터 는 malloc()를 사용 하기에 매우 적합 합 니 다.new 로 분 배 된 메모리 로 멈 춰 서 도대체 delete,delete[]를 사용 하 는 지 생각해 야 합 니 다.:delete,:delete,:delete[]중의 어느 방출,malloc()가 분 배 된 메모리 에 대해 생각 할 필요 가 없습니다.free()는 천 하 를 책임 집 니 다.하물며 사람들 은 realloc()가 있어 서 편리 하 게 메모 리 를 재 조정 할 수 있 습 니 다.당신 은'renew'가 있 습 니까?한 마디 로 말 하면 malloc()는 확실히 존재 할 필요 가 있 습 니 다.다음 에 우리 가 어떻게 포장 하 는 지 보 겠 습 니 다.코드 를 보 세 요.
// T : ( )
// MAX_CACHE_SIZE : , sizeof(T) , ,
// buffer
template<class T, size_t MAX_CACHE_SIZE = 0>
class CBufferPtrT
{
public:
explicit CBufferPtrT(size_t size = 0, bool zero = false) {Reset(); Malloc(size, zero);}
explicit CBufferPtrT(const T* pch, size_t size) {Reset(); Copy(pch, size);}
//
CBufferPtrT(const CBufferPtrT& other) {Reset(); Copy(other);}
template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other) {Reset(); Copy(other);}
~CBufferPtrT() {Free();}
T* Malloc(size_t size = 1, bool zero = false)
{
Free();
return Alloc(size, zero, false);
}
T* Realloc(size_t size, bool zero = false)
{
return Alloc(size, zero, true);
}
void Free()
{
if(m_pch)
{
free(m_pch);
Reset();
}
}
template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
{
if((void*)&other != (void*)this)
Copy(other.Ptr(), other.Size());
return *this;
}
CBufferPtrT& Copy(const T* pch, size_t size)
{
Malloc(size);
if(m_pch)
memcpy(m_pch, pch, size * sizeof(T));
return *this;
}
// buffer
template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
{
if((void*)&other != (void*)this)
Cat(other.Ptr(), other.Size());
return *this;
}
// buffer
CBufferPtrT& Cat(const T* pch, size_t size = 1)
{
size_t pre_size = m_size;
Realloc(m_size + size);
if(m_pch)
memcpy(m_pch + pre_size, pch, size * sizeof(T));
return *this;
}
template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
{
if((void*)&other == (void*)this)
return true;
else if(m_size != other.Size())
return false;
else if(m_size == 0)
return true;
else
return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
}
bool Equal(T* pch) const
{
if(m_pch == pch)
return true;
else if(!m_pch || !pch)
return false;
else
return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
}
T* Ptr() {return m_pch;}
const T* Ptr() const {return m_pch;}
T& Get(int i) {return *(m_pch + i);}
const T& Get(int i) const {return *(m_pch + i);}
size_t Size() const {return m_size;}
bool IsValid() const {return m_pch != 0;}
// ,
operator T* () {return Ptr();}
operator const T* () const {return Ptr();}
// ,
T& operator [] (int i) {return Get(i);}
const T& operator [] (int i) const {return Get(i);}
bool operator == (T* pv) const {return Equal(pv);}
template<size_t S> bool operator == (const CBufferPtrT<T, S>& other) {return Equal(other);}
//
CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);}
template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, S>& other) {return Copy(other);}
private:
void Reset() {m_pch = 0; m_size = 0; m_capacity = 0;}
size_t GetAllocSize(size_t size) {return max(size, min(size * 2, m_size + MAX_CACHE_SIZE));}
T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
{
if(size >= 0 && size != m_size)
{
size_t rsize = GetAllocSize(size);
if(size > m_capacity || rsize < m_size)
{
m_pch = is_realloc ?
(T*)realloc(m_pch, rsize * sizeof(T)) :
(T*)malloc(rsize * sizeof(T)) ;
if(m_pch || rsize == 0)
{
m_size = size;
m_capacity = rsize;
}
else
Reset();
}
else
m_size = size;
}
if(zero && m_pch)
memset(m_pch, 0, m_size * sizeof(T));
return m_pch;
}
private:
T* m_pch;
size_t m_size;
size_t m_capacity;
};
// buffer typedef
typedef CBufferPtrT<char> CCharBufferPtr;
typedef CBufferPtrT<wchar_t> CWCharBufferPtr;
typedef CBufferPtrT<unsigned char> CByteBufferPtr;
typedef CByteBufferPtr CBufferPtr;
#ifdef _UNICODE
typedef CWCharBufferPtr CTCharBufferPtr;
#else
typedef CCharBufferPtr CTCharBufferPtr;
#endif
응.여기 서 왜 두 개의 복사 구조 함수 와 할당 연산 자 를 다시 불 러 와 야 하 는 지 설명해 야 한다.먼저 컴 파일 러 는 서로 다른 템 플 릿 매개 변수 로 서로 다른 종 류 를 생 성 한다.즉,CBufferPtrT이 패 키 징 재 에 만족 합 니 다.이것 은 일반적인 malloc()패키지 가 아니 라'색인 접근 을 지원 하 는 형식 이 안전 한 동적 버퍼'로 볼 수 있 습 니 다.이 를 하나의 socket 통신 클래스 에 멤버 속성 으로 두 면 여러 스 레 드 와 여러 방법 으로 접근 하 는 수신 버퍼 와 버퍼 를 보 내 는 역할 이 가장 적합 합 니 다(당연히 스스로 동기 화 해 야 합 니 다).여러분 은 아래 의 테스트 예 를 시험 해 보고 그것 의 용법 을 알 아 볼 수 있 습 니 다.
new & delete
뉴 의 패키지 하면 바로 생각 나 는 게 스마트 지침 이 죠!맞 아,바로 스마트 포인터 야.하지만 STL 이 제공 하 는 autoptr 결함 이 많 습 니 다.먼저 사용 하기 불편 합 니 다.이런 쓰기 도 지원 하지 않 습 니 다."std:auto"ptr
int _tmain(int argc, _TCHAR* argv[])
{
CBufferPtr buffer;
unsigned char c1 = 'X';
unsigned char pc1[] = "123";
unsigned char pc2[] = "abc";
buffer.Cat(&c1);
buffer.Cat(pc1, 3);
buffer.Cat(pc2, 3);
CBufferPtrT<unsigned char, 10> buffer2 = buffer;
buffer2.Cat(buffer);
buffer2.Realloc(0);
unsigned char* pc = buffer;
const unsigned char& c = buffer[5];
buffer[5] = 'O';
short i1 = 0x7FFF;
short pi0[] = {9,9,9};
short pi1[] = {1,2,3};
short pi2[] = {4,5,6};
short pi3[] = {8,8,8};
CBufferPtrT<short, 10> bufferS(pi0, 3);
bufferS.Cat(&i1);
bufferS.Cat(pi1, 3);
bufferS.Cat(pi2, 3);
bufferS.Cat(pi3, 3);
CBufferPtrT<short, 5> bufferS2;
bufferS2.Malloc(4);
bufferS2 = bufferS;
bufferS2.Realloc(30);
CBufferPtrT<int> bufferI(5, true);
for(size_t i = 0; i < bufferI.Size(); i++)
bufferI[i] = i *10;
bufferI.Malloc();
bufferI[0] = 123;
// ,
// bufferI = bufferS;
return 0;
}
-------------------------------------------------------------------------------- 후기•메모리 관리 에 대해 서 는 아직 말 하지 않 은 것 이 있 습 니 다.vetor,list,map 같은 용기 의 지침 을 우아 하 게 관리 하 는 방법 입 니 다.이 화 제 는 나중에 STL 을 토론 할 때 상세 하 게 설명 하 겠 습 니 다.이 자리 의 코드 에 서 는 free/delere 라 는 단어 가 거의 보이 지 않 습 니 다(new 는 있 습 니 다―스마트 포인터 에 값 을 부여 할 때^ ^).이 자리 의 경험 에 따 르 면 패 키 징 을 적절하게 이용 하면 많은 번 거 로 움 을 줄 이 고 코드 를 더욱 뚜렷 하고 조리 있 게 하 며 오류 발생 확률 을 낮 출 수 있 습 니 다.물론 포장 은 만능 이 아니다.모든 문 제 를 해결 할 수 없다.관건 은 개인의 집중 과 세심 함 이다.이 코드 글 자 는 자신의 관점 을 제시 하 는데 벽돌 을 던 져 옥 을 끌 어 올 리 고 사람들 로 하여 금 좋 은 프로 그래 밍 습관 을 어떻게 키 우 는 지 생각 하 게 하 는 데 목적 을 둔다.권위 가 아니 라 믿 을 수도 없다.가장 진실 한 지식 은 개인의 가장 직접적인 체험 에서 나 와 야 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Visual Studio에서 파일 폴더 구분 (포함 경로 설정)Visual Studio에서 c, cpp, h, hpp 파일을 폴더로 나누고 싶었습니까? 어쩌면 대부분의 사람들이 있다고 생각합니다. 처음에 파일이 만들어지는 장소는 프로젝트 파일 등과 같은 장소에 있기 때문에 파일...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.