[이것이 C++이다] Part 01. C에서 C++로 문법 전환하기 (2)

1.5 메모리 동적 할당

  • malloc()과 포인터
    • malloc()은 주어진 크기(size_t)만큼 메모리를 빌려 주소를 반환하는 함수로 메모리의 “크기” 외 신경을 쓰지 않는다. 용도? malloc() 관점에서는 중요하지 않다.
    • 포인터는 가리키는 대상 보다 접근자의 해석이 더 중요한 문법이다. 예를 들어, int형 포인터 변수(int *a)는 가리키는 대상이 어떠하든 int 형태로 해석한다.

즉, malloc()과 포인터는 사용자의 자유도가 높은 문법으로 “잘 알고 사용”해야 한다.

new, delete

  • new 연산자는 malloc()과 다르게 메모리 크기를 정하지 않는다는 특징이 있다.

    #include<iostream>
    
    int main() {
      // 인스턴스만 동적으로 생성하는 경우
      int *pdata = new int;
    
      // 초깃값을 기술하는 경우
      int *pNewData = new int(10);
    
      *pdata = 5;
    
      std::cout << *pdata << std::endl;
      std::cout << *pNewData << std::endl;
        
      delete pdata;
      delete pNewData;
    
      // 5
      // 10
    }
    
  • 배열 형태로 동적 생성한 것은 반드시 배열 형태로 삭제해야 한다.

    int *arr = new int[5];
    delete[] arr;
    
  • new 연산자는 객체의 생성자를 호출하고, delete 연산자는 객체의 소멸자를 호출한다.

1.6 참조형

참조자

  • 변하지 않는 포인터와 같다. (scope 끝까지)
  • 반드시 선언과 동시에 초기화 해야 한다.
  • 상수에는 포인터를 선언할 수 없는 것 처럼 참조도 사용할 수 없다.
  • 포인터와 참조는 내부 구조상으로 동일하다.

    int ndata(10);
    int &rdata(ndata);  // rdata, ndata 이 두 주소가 같다.
    rdata = 5;  // 원본인 ndata값이 5로 바귄다. 
    int *pdata = &3;  // 포인터는 상수를 가리킬 수 없으므로 불가능
    int &rdata(3);  // 상수에 대한 참조는 불가능
    int &rdata;  // 참조 원본이 없으므로 불가능
    

    참조자를 왜 쓰는데?

  • 덩치 큰 자료는 값을 넘겨주는 것 보다 주소(C++에서는 ‘참조’)를 넘겨 주는 것이 효율적이다.

    void TestFunc(int &rParma) {
      rParam = 100;  // 원본 변경
    }
    int main() {
      int nData = 0;
      TestFunc(nData);  // 여기서는 매개변수가 참조인지 알 수 없다.
      std::cout << nData << std::endl;
    }
    // 100
    
  • r-value 참조
    • 곧 사라질 대상에 대해 참조자를 부여한다.
    • 변수와 상수 포함.
    int &&rdata = 3+4;
    

1.7 범위 기반 for문

  • 참조자 형식으로 선언해야 for문 내에서 배열의 요소 변경이 가능하다.

    int nlist[5];
    for (auto n : nlist) {
      // nlist 접근만 가능
    }
    for (auto &n : nlinst) {
      // nlist 변경 가능
    }