C ++/C++ 정리

동적할당 new와 업캐스팅, 다운캐스팅

무면허 개발자 2023. 1. 10. 16:36

 

( 본 게시글은 작성자가 메모용으로 사용하는 용도임을 밝힙니다. 정보가 불확실할 수 있으니 참고 부탁드립니다 )

new

 

C++이전의 C에서는 동적할당을 하기 위해서는 malloc 키워드를 사용하여 동적 메모리를 할당하였다.

C++에서는 new라는 키워드로도 동적할당을 할 수 있다.

위의 객체 a는 malloc으로 메모리를 할당해줬고, 객체 b는 new를 사용하여 메모리를 할당했다. 둘다 동적 메모리를 할당하는 기능은 똑같으나, 2가지의 차이점이 있다.

하나는 malloc은 생성자를 호출하지 않는다는 점이다. 위는 객체 생성후 컴파일시 콘솔창에 나오는 출력창인데, 부모와 자식 생성자가 하나씩만 호출된것을 알 수 있다. 반대로 new 키워드는 생성자를 호출한다. Child만큼 힙 영역에 메모리를 할당후 포인터 변수 b에 메모리의 주소값을 리턴하는 형식이다. new는 주소값을 리턴하기때문에 new를 사용할때는 포인터형식의 변수를 사용해야한다.

또 다른 차이점으로는 new는 c++이 만들어졌을때 같이 만들어진 키워드지만, malloc은 c가 나온뒤에 나중에 만들어진 키워드다. 호환이 잘 안될수도 있다.

malloc은 할당을 할때 malloc 앞에 형변환 키워드를 설정해야줘야하고, stdlib.h이라는 헤더를 따로 추가해줘야하므로, 가급적이면 new를 쓰도록 해야 한다.

 

============================================================================

 

업캐스팅, 다운캐스팅

 

업캐스팅은 자식클래스 부모클래스의 형식을 병행하여 같이 사용하기 위해 사용한다. 업캐스팅을 하면 상위 클래스와 하위 클래스 두 클래스를 사용할수 있다고 할 수 있다.

먼저 부모, 자식 클래스가 될 클래스를 만들고, 생성자와 소멸자를 정해주고, 그리고 Print라는 함수를 각자 만들어준다.

위의 Allocation을 참조하는 포인터 b에 new Child를 통해 업캐스팅을 해준다. 이후에 포인터 b에서 역참조하여 Print 함수를 출력하면

밑처럼 부모 -> 자식 순으로 생성자가 호출되고, Print함수가 호출된다. 그리고 소멸 순서는 자식->부모로 하위에서 상위 클래스 순서로 이뤄져야하지만, 위 출력창은 부모 소멸자만 호출되고, 자식 소멸자는 호출되지 않았음을 볼 수 있다.

이는 업 캐스팅이 진행되고 나서 별 다른 조치가 없다면 부모 소멸자만 호출되고, 자식 소멸자는 호출되지 않아 메모리가 소멸되지 않는 상태에서 메모리 누수로 이어질 수 있으므로, 자식 소멸자도 호출시켜줘야 한다. 이때 사용하는 것이 다운캐스팅과 virtual 키워드이다.

위 Child* c = (Child*)b;를 통해 다운 캐스팅이 되었다. 기존의 포인터 b의 주소값을 포인터 c에 넣어주는 것인데, 이때 Child로 형변환을 시키면서 다운캐스팅이 진행되는 것이다.

그리고 delete b가 아닌 delete c로 바꿔주고 출력해보면, 소멸자가 자식과 부모 전부 호출되는 것을 볼 수 있다.

virtual 키워드의 방법도 있는데, 이때는 부모 클래스의 소멸자 앞에 virtual 키워드만 넣어주면 하위 클래스의 소멸자를 전부 호출시킬 수 있다.

이번에는 다중 상속을 진행하고, 다중 상속된 클래스들의 생성자 호출의 순서와, 소멸자 호출 순서를 보겠다.

위의 부모 클래스 Allocation에 그 밑 자식 클래스로 Child,Son,Son2를 준비한다.

Allocation 포인터 b,c,s,를 각각 자식클래스들로 업캐스팅을 해준다. 그리고 포인터의 주소값을 저장하는 배열을 만들어주고, 주소값을 저장한다. 소멸자 호출은 for문을 통하여 각 배열의 원소의 소멸자를 각각 호출시키게 해준다.

컴파일을 눌러보면 Allocation* b = new Child 때 부모, 자식 생성자 Allocation* c = new Son때는 부모,자식,손자 생성자 Allocation* s = new Son2때는 부모,자식,손자,증손자 생성자가 각각 호출된것을 볼 수 있다.

그리고 for문을 통하여 각 원소의 소멸자를 호출하는데, 이때 보면 자식,부모 손자,자식,부모 등등 중복되는 자식,부모 소멸자가 보인다. 이것은 생성된 b,c,s같은 각각의 객체의 생성자가 같은 부모,자식 생성자라고 하더라도 각 객체마다 생성자는 다르다고 봐야하기때문에 소멸 또한 개별적으로 진행시켜줘야 하는것이다. 그렇지 않으면 위에 설명한거처럼 메모리 누수가 일어나게 된다.

위는 부모 클래스 소멸자앞에 virtual 키워드를 붙이지 않았을때의 출력창이다.

설명한대로 virtual을 붙이지 않으면 각 객체의 부모 소멸자만 호출되기 때문에, 메모리가 남아 쌓이고 나면 누수가 일어난다.