c++ 언어 A 부터 Z까지 - 다섯번째

 


인사 


안녕하세요. 오늘은 C++ 언어를 공부하면서 새롭게 알게 된 모듈과 함수에 대해 정리해 보려 합니다. 이번 포스팅에서 다룰 주제는 바로 std::arraystd::vector의 조합입니다.

문제의 시작


아래는 제가 처음 시도했던 코드입니다. vector 안에 int[MAX_N] 형태의 배열을 넣으려 했습니다:

#include <stdio.h>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#define MAX_N 100

void InputVector(int n, std::vector<int [MAX_N]>& v) {
 
  int i;
 
  for (i = 0; i < n; i++) {
   
    // input
    std::string s;
    getline(std::cin, s);
   
    std::istringstream iss(s);
    int value, j = 0;
    int arr [MAX_N];
   
    while (iss >> value) {
      arr[j++] = value;    
    }
   
    v.push_back(arr);
  }
 
}

void Answer(int n, std::vector<int [MAX_N]>& v) {
 
  for (auto a : v) {
   
    int answer = 0;
    for (int i = 0; i < n; i++) {
      answer += a[i];
    }
    printf("%d\n", answer);
  }
 
}

int main(void) {
 
  // n
  int n;
  scanf("%d", &n);
 
  getchar();
 
  // vector
  std::vector<int [MAX_N]> v;
  v.reserve(n);
  InputVector(n, v);
 
  // answer
  Answer(n, v);
 
  return 0;
}

하지만 컴파일 에러가 발생했습니다. 이유를 조사해보니 vector는 내부적으로 타입 T의 복사 생성자와 대입 연산자가 필요하다는 것을 알게 되었습니다. 그런데 C++의 기본 배열 타입(int[])은 이러한 기능을 제공하지 않기 때문에 문제가 생겼던 것입니다.

또한, 배열은 포인터로 해석되기 때문에 같은 배열처럼 보여도 실제로는 복사가 아닌 주소 참조가 일어납니다. 이로 인해 vector에 push할 때 모든 요소가 동일한 배열을 참조하는 심각한 오류가 발생하게 됩니다.


#include <stdio.h>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>

void InputVector(int n, std::vector<std::string>& v) {
 
  int i;
  for (i = 0; i < n; i++) {
   
    std::string s;
    getline(std::cin, s);
   
    v.push_back(s);
  }
 
}

void Answer(std::vector<std::string>& v) {
 
  for (auto a : v) {
   
    std::istringstream iss(a);
   
    int value, answer = 0;
   
    while (iss >> value) {
      answer += value;  
    }
    printf("%d\n", answer);
   
  }
 
}

int main(void) {
 
  // n
  int n;
  scanf("%d", &n);
 
  getchar();
 
  // vector
  std::vector<std::string> v;
  InputVector(n, v);
 
  // answer
  Answer(v);
 
  return 0;
}


사실 이렇게 짜는게 가장 효율적인것 같긴 합니다... 

해결책 


보다 근본적인 해결책은 C++의 std::array를 사용하는 것이었습니다. std::array<int, MAX_N>는 고정 크기의 배열처럼 작동하지만 STL 컨테이너의 특징도 가지고 있어서, vector와 함께 사용할 수 있습니다.

아래는 개선된 코드입니다:

#include <stdio.h>
#include <vector>
#include <array>
#include <string>
#include <iostream>
#include <sstream>
#define MAX_N 100

void InputVector(int n, std::vector<std::array<int, MAX_N>>& v, std::array<int, MAX_N>& arr ,int* cnt) {
 
  int i;
  for (i = 0; i < n; i++) {
    cnt[i] = 0;
  }
 
  for (i = 0; i < n; i++) {
   
    std::string s;
    getline(std::cin, s);
   
    std::istringstream iss(s);
    int value;
    while (iss >> value) {
      arr[cnt[i]] = value;
      cnt[i] += 1;
    }
   
    v.push_back(arr);
  }
 
}

void Answer(int* cnt, std::vector<std::array<int, MAX_N>>& v) {
 
  int i = 0;
  for (auto a : v) {
   
    int n = cnt[i++];
    int j, answer = 0;
   
    for (j = 0; j < n; j++) {
      answer += a[j];
    }
    printf("%d\n", answer);
   
  }
 
}

int main(void) {
 
  // n
  int n;
  scanf("%d", &n);
 
  getchar();
 
  // vector
  std::vector<std::array<int, MAX_N>> v;
  std::array<int, MAX_N> arr;
  int* cnt = new int [n];
  InputVector(n, v, arr, cnt);
 
  // answer
  Answer(cnt, v);
 
  // delete
  delete[] cnt;
 
  return 0;
}

이렇게 하면 배열을 안전하게 복사하고 저장할 수 있고, vector의 장점도 그대로 활용할 수 있습니다. 또한 std::arraynew로 할당한 배열과 달리 명시적인 delete[] 처리가 필요 없어 메모리 관리가 한결 간단합니다.


그렇다면 항상 std::array를 사용하는 것이 정답일까요? 이에 대한 궁금증을 해결하기 위해 두 방식의 장단점을 비교해 보았습니다.

일반적으로 new int[]를 사용하면 런타임에 크기를 유동적으로 지정할 수 있고, 상대적으로 더 많은 데이터를 다룰 수 있다는 장점이 있습니다. 반면 std::array는 정해진 크기의 배열이지만 복사와 대입이 안전하게 이루어지고, STL 컨테이너들과의 호환성도 뛰어납니다.

결국 중요한 건 상황에 맞는 선택입니다. 메모리 크기나 유연성이 중요한 경우엔 new 방식이 유리하고, 안정성과 관리 편의성을 중시한다면 std::array가 더 적합할 수 있습니다.

필요에 따라 가장 적절한 도구를 선택하는 것이 중요한 포인트였습니다.


마무리


오늘은 vector에 배열을 넣으려다 생긴 문제와 이를 std::array로 해결하는 과정을 정리해보았습니다. 단순히 동작하는 코드가 아닌, C++ 언어의 특성과 컨테이너 사용법에 대해 한층 깊이 이해할 수 있는 계기가 되었습니다.

앞으로도 공부하면서 얻은 인사이트나 실수, 해결 방법들을 기록해 나가려 합니다. 이 글이 같은 고민을 가진 분들께 도움이 되길 바라며, 여러분의 개발도 항상 좋은 방향으로 나아가길 응원합니다.

화이팅입니다.






 

개발자 김동완

개발, 비전공, 백엔드, 풀스택, 프로그래밍

댓글 쓰기

다음 이전