본문 바로가기
전문가를 위한 C++

2025/03/03 ch.07 전문가답게 C++ 코딩하기

by 딴짓거리 2025. 3. 3.

 

C++ 프로그래밍은 도로가 없는 곳을 운전하는 것과 같다

원하는 곳을 마음껏 갈 수 있지만 안전장치가 없어 주의하지 않으면 위험한 상황에 빠질 수 있다

C++는 프로그래머가 모든 상황을 잘 알고 있다고 가정하고 자유를 최대한 보장함

성능 >>> 안정성

 

메모리 할당과 관리는 C++ 프로그래밍에서 특히 문제가 발생하기 쉬운 영역

 

모든 C++에선 로우 레벨 메모리 연산을 최대한 피하는 추세

 

7.1 동적 메모리 다루기

7.1.1 메모리의 작동 과정 살펴보기

로컬변수<- 자동변수. 이 변수가 선언된 스코프를 벗어나면 할당된 메모리가 자동으로 해제 됨

 

new 키워드를 사용하면 프리스토어에 메모리가 할당되고

포인터 변수를 스택에 생성하고 초기화한 뒤 프리스토어에 할당된 메모리를 포인터 변수가 가리킴

 

7.1.2 메모리 할당과 해제

변수가 사용할 공간은 new로 생성. delete로 해제

 

new에 변수의 타입을 지정해서 호출하면 할당된 메모리에 대한 포인터가 리턴됨

# new의 리턴값을 무시하거나 그 포인터를 담았던 변수가 스코프를 벗어나게 되면 미아가 됨. 메모리 누수

 

malloc()

c에서 사용. 인수로 지정한 바이트 수만큼 메모리를 할당

간편하고 이해하기도 쉬운것 같지만 new를 이용하는것이 바람직함. new는 객체까지 만들어줌

 

free()도 비슷

free는 객체의 소멸자를 호출하지 않지만 delete는 소멸자를 호출함

 

new도 실패할 수 있다. 메모리가 부족한 드문 경우에 익셉션을 던지고 이걸 안잡으면 프로그램이 종료됨

 

7.1.3 배열

배열을 프리스토어에 할당하면 배열의 크기를 실행 시간에 정할 수 있다는 장점이 있다

new[]를 호출한 만큼 delete[]를 호출해야함을 주의

동적으로 할당된 배열은 동적배열과 다르니 주의

# realloc()은 새로 지정한 크기에 맞게 메모리 블록을 새로 할당하는 방식으로 배열의 크기를 동적으로 조절함

기존 데이터를 새 블록으로 복사하고 원래 블록은 삭제한다

사용자가 정의한 객체는 비트단위 복사 작업을 하면 매우 위험하므로 realloc()은 절대 사용하지 말자

 

다차원배열을 동적할당할때 보통 첫번째 차원에 대한 메모리를 할당하고 for문으로 하위 배열에 대한 메모리를 할당하는데, 이러면 프리스토어 내에서 메모리 블록이 여러곳으로 흩어진다. 이렇게 되면 알고리즘의 성능에 영향을 미치므로, 일차원 배열로 선언 후, 수식을 통해 각 원소에 접근하는편이 낫다

 

c스타일 배열은 메모리 안전성이 떨어지므로 가급적 사용하지 말자

 

7.1.4 포인터 다루기

포인터는 메모리 주소일 뿐 그 주소를 얼마든지 변경할 수 있어서 잘못 사용하면 메모리가 손상되는등의 위험한 일이 생길 수 있다

 

포인터에 대한 타입 캐스팅

포인터는 단지 메모리 주소에 불과해서 타입을 엄격히 따지지 않는다

포인터의 타입은 C 스타일 캐스팅으로 얼마든지 바꿀 수 있다

 

정적 캐스팅( static_cast<T>)을 사용하면 좀더 안전함. 관련 없는 테이터 타입으로 포인터를 캐스팅하면 컴파일 에러를 만들어줌

정적 캐스팅하려는 포인터와 캐스팅 결과에 대한 포인터가 가리키는 객체가 서로 상속관계에 있다면 컴파일 에러발생X

이럴땐 동적 캐스팅(dynamic_cast<T>)을 사용하는게 더 안전.

 

배열? 포인터?

프리스토어에 할당된 배열은 첫 번째 원소를 가리키는 포인터로 참조

스택에 할당된 배열은 배열 문법([])으로 참조한다.

 

배열 = 포인터

프리스토어 배열 뿐만 아니라 스택 배열에 접근할 때도 포인터를 사용할 수 있다.

 

7.2.2 포인터가 모두 배열은 아니다

포인터와 배열은 비슷하지만 똑같은것은 아니다

 

7.3 로우레벨 메모리 연산

C++는 C에 비해 메모리에 대해 신경 쓸 일이 적다

 

7.3.1 포인터 연산

C++컴파일러는 포인터 연산을 수행할 때 포인터에 선언된 타입을 적용한다

int로 선언된 포인터를 1만큼 증가시키면 메모리에서 1바이트가 아니라 int(보통 4 바이트) 크기만큼 이동한다

배열을 다루는게 유용

 

7.3.2 커스텀 메모리 관리

리소스가 제한적이거나 공유메모리 관리같은 특수한 경우 메모리를 직접 다뤄야 함

클래스에 큰 덩어리의 메모리를 할당해놓고 필요할 때마다 잘라 쓰는 것

객체 크기를 사전에 알고 있다면 오버헤드를 줄일 수 있다

 

7.3.3 가비지 컬렉션

가비지 컬렉션을 제공하는 환경이라면 프로그래머가 객체에 할당된 메모리를 직접 해제할 일이 거의 없다

더이상 참조하지 않는 객체는 런타임 라이브러리가 일정한 시점에서 제거

 

C++에선 자바나C#과 달리 가비지 컬렉션 제공X

모던 C++에선 스마트 포인터로 메모리 관리 가능

 

가비지 컬렉션의 단점

가비지 컬렉터가 작동하는 동안 프로그램이 응답하지 않을 수 있음

비 결정적 소멸자를 사용하기 때문에 객체는 가비지  컬렉션 되기 전에는 제거되지 않으므로 소멸자의 수행 시점이 불분명해짐

 

7.3.4 객체 풀

가비지 컬렉션의 정상화..

 

 

~354p