18. Vectors and Arrays

18.1. Introduction

std::vector는 어떻게 카피될까?

18.2. Initialization

초기화는 보통 생성자에 std::initializer_list를 사용한다. 이는 값으로 전달된다.

          vector(std::initializer_list<double> lst)                  // initializer-list constructor
                    :sz{lst.size()}, elem{new double[sz]}   // uninitialized memory
                                                                                         // for elements
          {
                    std::copy( lst.begin(),lst.end(),elem);    // initialize (using std::copy(); §B.5.2)
          }

18.3. Copying

내부에 포인터를 통해 메모리를 동적 할당하고 있는 클래스를 복사할 때에는 기본 복사 생성자를 사용하면 포인터가 값으로 복제되어 두 오브젝트의 내부 포인터가 같은 메모리를 가리키게 되는 문제가 생긴다.

18.3.1. Copy constructors

그래서 내부에 포인터를 통해 메모리를 동적 할당하는 클래스는 반드시 복사 생성자를 만들어줘야 한다.

vector:: vector(const vector& arg)
// allocate elements, then initialize them by copying
          :sz{arg.sz}, elem{new double[arg.sz]}
{
          std::copy(arg,arg+sz,elem);  // std::copy(); see §B.5.2
}

18.3.2. Copy assignments

복사 대입 연산자도 만들어줘야 한다. 자가 대입 케이스에 주의하라.

vector& vector::operator=(const vector& a)
          // make this vector a copy of a
{
          double* p = new double[a.sz];                // allocate new space
          std::copy(a.elem,a.elem+a.sz,elem);             // copy elements
          delete[] elem;                                              // deallocate old space
          elem = p;                                                     // now we can reset elem
          sz = a.sz;
          return *this;                             // return a self-reference (see §17.10)
}

18.3.3. Copy terminology

얕은 복제는 포인터만을 복제한다. 깊은 복제는 포인터가 가리키는 내용물을 복제한다.

18.3.4. Moving

복제가 필요하지 않을 때에는 이동 생성자와 이동 대입 연산자를 구현한다.

vector::vector(vector&& a)
          :sz{a.sz}, elem{a.elem}                // copy a’s elem and sz
{
          a.sz = 0;                                          // make a the empty vector
          a.elem = nullptr;
}

vector& vector::operator=(vector&& a) // move a to this vector
{
          delete[] elem;                                // deallocate old space
          elem = a.elem;                              // copy a’s elem and sz
          sz = a.sz;
          a.elem = nullptr;                          // make a the empty vector
          a.sz = 0;
          return *this;                                 // return a self-reference (see §17.10)
}

18.4. Essential operations

클래스가 제공해야 하는 기본적인 연산들은 다음과 같다.

  • 하나 이상의 인자에 대한 생성자
  • 기본 생성자
  • 복사 생성자
  • 복사 대입 연산자
  • 이동 생성자
  • 이동 대입 연산자
  • 소멸자

기본 생성자는 의미 있는 기본값으로 클래스를 초기화시킬 수 있을 때 만든다. 소멸자는 클래스가 생성자에서 자원을 획득할 때 이를 해제하기 위해 만든다. 이 경우 거의 항상 복사 생성자, 복사 대입 연산자, 이동 생성자, 이동 대입 연산자가 필요하다. 파생 클래스가 소멸자가 필요한 경우 기반 클래스의 소멸자는 virtual로 선언한다.

18.4.1. Explicit constructors

인자가 하나인 생성자의 경우 explicit을 달아 묵시적 형변환을 막을 수 있다.

18.4.2. Debugging constructors and destructors

생성자나 소멸자, 대입 연산자 등에 로깅을 추가해 디버깅할 수 있다.

18.5. Access to vector elements

배열 접근자를 쓰기 위해서는 operator[]를 정의해야 한다.

class vector {
          // . . .
          double& operator[ ](int n) { return elem[n]; }        // return reference
};

18.5.1. Overloading on const

const vector에 대한 배열 접근자를 쓰기 위해서는 이를 const로 오버로딩해야 한다.

class vector {
          // . . .
          double& operator[](int n);             // for non-const vectors
          double operator[](int n) const;      // for const vectors
};

18.6. Arrays

C 스타일 배열도 쓸 수 있다. 가능한 한 std::vector를 써야 하지만, 오래된 코드의 유지보수를 위해 알아 놓도록 하자. 배열은 범위 체크를 하지 않는다.

18.6.1. Pointers to array elements

포인터는 배열 원소에 접근할 수 있으며 배열처럼 [] 연산자를 쓸 수 있다. 범위 체크는 사용자의 몫이다.

18.6.2. Pointers and arrays

배열은 sizeof을 제외한 부분에서 평가될 때 배열의 첫 번째 원소를 가리키는 포인터로 변환된다. 배열끼리는 대입될 수 없다.

18.6.3. Array initialization

char의 배열은 문자열 리터럴로 초기화될 수 있다. 이 때 배열의 길이는 문자열의 길이 + 1이다. 문자열 리터럴로 초기화하지 않을 경우에는 해당하지 않으며 이 때는 C 스타일 문자열이 아니게 된다.

18.6.4. Pointer problems

  • 널 포인터로 접근하지 말라. 포인터를 전달하는 경우 널 포인터를 체크하라.
  • 포인터를 항상 초기화하라.
  • 존재하지 않는 배열의 원소를 접근하지 마라.
  • delete된 포인터에 접근하지 마라.
  • 지역 변수에 대한 포인터나 참조자를 반환하지 마라.

18.7. Examples: palindrome

18.7.1. Palindromes using string

std::string을 통해 회문 체크를 할 수 있다.

18.7.2. Palindromes using arrays

배열을 통해 회문 체크를 할 수 있다.

18.7.3. Palindromes using pointers

포인터를 통해 회문 체크를 할 수 있다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중