포인터는 왜 사용하지?

연어는결국강으로·2024년 11월 25일
0

CPP 정복기

목록 보기
1/3

최근에 컴퓨터 그래픽스 공부를 하고 싶어서 C++를 배우고 있다. 포인터를 공부하다가 의문이 들었다. 굳이 이걸 쓰는 이유가 뭘까? 그 의문은 아래와 같은 예에서 시작했다.

자바의 경우 이런식으로 하면 끝나는 코드인데...

public MyClass create() {
    return new MyClass();
}

public static void main(String[] args) {
    MyClass c = create();
}

음... C++에서 똑같이 해도 되는걸까?

MyClass create() {
    return MyClass();  // 스택에 생성된 객체 반환
}

int main() {
    MyClass c = create();
}

되는거 같은데....? 그래서 GPT선생님께 물어봤다. 답은 위의 예에서 자바의 경우는 내가 만든 MyClass가 처음부터 heap에 생성되지만, cpp에서 만든 MyClass는 stack에 생성된다는 것이었다. create 함수가 종료되면 이것은 깊은 복사(그러니까 새로운 MyClass를 하나 만든다는 소리)를 heap에 만들고 main 함수의 c로 전달이 된다.

아.... 그러니까 되긴 되는데..... 깊은 복사가 일어나니까... 엄청난 낭비가 될 것이다. 물론 최신 버전의 cpp에서는 RVO(Return Value Optimization) 라는게 있어서 실제 복사가 생략된다고는 한다. 그런데 경험해본 것처럼 대부분의 시스템은 레거시이니까... 조심해야겠다.

더 나아가서 포인터를 왜 사용하는지 알아보자.

1. 객체의 수명제어

  • 반환된 객체의 수명은 함수 블록 밖에서도 유지된다. 스택 객체를 반환하는 경우, 스택 프레임이 소멸하면 객체도 소멸한다.
  • 힙 메모리에서는 객체의 수명을 개발자가 직접 관리할 수 있다:
MyClass* create() {
    return new MyClass();  // 힙에 객체 생성
}

int main() {
    MyClass* c = create();  // 포인터로 반환된 객체를 제어
    delete c;  // 메모리 해제 필요
}

이렇게 객체의 소멸자를 사용해서 메모리 해제가 되는듯하다. - 아직 배우지 않음 -

2. 동적 데이터 구조

  • 복잡한 데이터 구조(예: 트리, 링크드 리스트)는 런타임에 동적으로 메모리를 할당해야 한다.
  • 이런 경우에는 반드시 포인터를 사용해야 한다.

3. 다형성

  • 다형성을 활용하려면 포인터를 사용해야 한다.
class Animal {
public:
    virtual void speak() { std::cout << "Animal\n"; }
};

class Dog : public Animal {
public:
    void speak() override { std::cout << "Dog\n"; }
};

Animal* createAnimal() {
    return new Dog();  // 부모 타입 포인터로 자식 객체 참조
}

0개의 댓글