챕터7. 실습 문제

2025. 3. 28. 08:52Programming Languages/C++

7.6 실습 문제

문제 1: 범용 스마트 배열 템플릿

다양한 타입에 대해 동작하는 동적 배열 클래스를 구현하세요:

#include <iostream>
#include <stdexcept>
#include <initializer_list>

template <typename T>
class SmartArray {
private:
    T* data;
    size_t size;
    size_t capacity;
    
    // 용량 확장
    void expand(size_t newCapacity) {
        if (newCapacity <= capacity) return;
        
        T* newData = new T[newCapacity];
        
        // 기존 데이터 복사
        for (size_t i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        
        // 기존 메모리 해제
        delete[] data;
        
        // 새 데이터로 교체
        data = newData;
        capacity = newCapacity;
    }
    
public:
    // 기본 생성자
    SmartArray() : data(nullptr), size(0), capacity(0) {}
    
    // 크기 지정 생성자
    explicit SmartArray(size_t initialSize) : size(initialSize), capacity(initialSize) {
        data = new T[capacity];
        
        // 기본값으로 초기화
        for (size_t i = 0; i < size; i++) {
            data[i] = T();
        }
    }
    
    // 초기화 리스트 생성자
    SmartArray(std::initializer_list<T> init) : size(init.size()), capacity(init.size()) {
        data = new T[capacity];
        
        // 초기화 리스트의 값으로 배열 초기화
        size_t i = 0;
        for (const T& val : init) {
            data[i++] = val;
        }
    }
    
    // 복사 생성자
    SmartArray(const SmartArray& other) : size(other.size), capacity(other.capacity) {
        data = new T[capacity];
        
        // 다른 배열의 데이터 복사
        for (size_t i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
    }
    
    // 이동 생성자
    SmartArray(SmartArray&& other) noexcept : data(other.data), size(other.size), capacity(other.capacity) {
        other.data = nullptr;
        other.size = 0;
        other.capacity = 0;
    }
    
    // 소멸자
    ~SmartArray() {
        delete[] data;
    }
    
    // 복사 대입 연산자
    SmartArray& operator=(const SmartArray& other) {
        if (this != &other) {
            // 기존 메모리 해제
            delete[] data;
            
            // 새 메모리 할당
            size = other.size;
            capacity = other.capacity;
            data = new T[capacity];
            
            // 데이터 복사
            for (size_t i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }
    
    // 이동 대입 연산자
    SmartArray& operator=(SmartArray&& other) noexcept {
        if (this != &other) {
            // 기존 메모리 해제
            delete[] data;
            
            // 리소스 이동
            data = other.data;
            size = other.size;
            capacity = other.capacity;
            
            // 원본 무효화
            other.data = nullptr;
            other.size = 0;
            other.capacity = 0;
        }
        return *this;
    }
    
    // 요소 접근 연산자
    T& operator[](size_t index) {
        if (index >= size) {
            throw std::out_of_range("인덱스가 범위를 벗어났습니다.");
        }
        return data[index];
    }
    
    const T& operator[](size_t index) const {
        if (index >= size) {
            throw std::out_of_range("인덱스가 범위를 벗어났습니다.");
        }
        return data[index];
    }
    
    // 배열 뒤에 새 요소 추가
    void push_back(const T& value) {
        if (size >= capacity) {
            // 용량이 부족하면 두 배로 확장
            expand(capacity == 0 ? 1 : capacity * 2);
        }
        
        data[size++] = value;
    }
    
    // 배열 뒤의 요소 제거
    void pop_back() {
        if (size > 0) {
            --size;
        }
    }
    
    // 지정된 위치에 요소 삽입
    void insert(size_t index, const T& value) {
        if (index > size) {
            throw std::out_of_range("삽입 위치가 유효하지 않습니다.");
        }
        
        // 용량이 부족하면 확장
        if (size >= capacity) {
            expand(capacity == 0 ? 1 : capacity * 2);
        }
        
        // 요소 이동
        for (size_t i = size; i > index; --i) {
            data[i] = data[i - 1];
        }
        
        // 새 요소 삽입
        data[index] = value;
        ++size;
    }
    
    // 지정된 위치의 요소 제거
    void erase(size_t index) {
        if (index >= size) {
            throw std::out_of_range("삭제 위치가 유효하지 않습니다.");
        }
        
        // 요소 이동
        for (size_t i = index; i < size - 1; ++i) {
            data[i] = data[i + 1];
        }
        
        --size;
    }
    
    // 배열 크기 반환
    size_t getSize() const {
        return size;
    }
    
    // 배열 용량 반환
    size_t getCapacity() const {
        return capacity;
    }
    
    // 배열이 비어 있는지 확인
    bool isEmpty() const {
        return size == 0;
    }
    
    // 배열 내용 출력
    void print() const {
        std::cout << "[ ";
        for (size_t i = 0; i < size; ++i) {
            std::cout << data[i];
            if (i < size - 1) {
                std::cout << ", ";
            }
        }
        std::cout << " ]" << std::endl;
    }
};

int main() {
    // 정수 배열 테스트
    SmartArray<int> intArray = {10, 20, 30, 40, 50};
    
    std::cout << "초기 정수 배열:" << std::endl;
    intArray.print();
    
    // 요소 추가
    intArray.push_back(60);
    intArray.push_back(70);
    
    std::cout << "요소 추가 후:" << std::endl;
    intArray.print();
    
    // 요소 삽입
    intArray.insert(2, 25);
    
    std::cout << "인덱스 2에 25 삽입 후:" << std::endl;
    intArray.print();
    
    // 요소 삭제
    intArray.erase(4);
    
    std::cout << "인덱스 4의 요소 삭제 후:" << std::endl;
    intArray.print();
    
    // 복사 생성자 테스트
    SmartArray<int> intArrayCopy = intArray;
    
    std::cout << "복사 생성된 배열:" << std::endl;
    intArrayCopy.print();
    
    // 원본 배열 변경
    intArray[0] = 100;
    
    std::cout << "원본 배열(변경 후):" << std::endl;
    intArray.print();
    
    std::cout << "복사된 배열(원본과 독립적):" << std::endl;
    intArrayCopy.print();
    
    // 문자열 배열 테스트
    SmartArray<std::string> stringArray;
    stringArray.push_back("Hello");
    stringArray.push_back("World");
    stringArray.push_back("C++");
    stringArray.push_back("Templates");
    
    std::cout << "\n문자열 배열:" << std::endl;
    stringArray.print();
    
    return 0;
}

문제 2: 함수 템플릿을 사용한 알고리즘 라이브러리 구현

다양한 타입의 배열과 컨테이너에서 사용할 수 있는 알고리즘 함수 템플릿 모음을 구현하세요:

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <type_traits>

// 배열의 모든 요소 출력
template <typename Container>
void printAll(const Container& container, const std::string& label = "컨테이너 내용") {
    std::cout << label << ": ";
    
    // 범위 기반 for 루프로 모든 요소 출력
    for (const auto& item : container) {
        std::cout << item << " ";
    }
    std::cout << std::endl;
}

// 배열에서 최댓값 찾기
template <typename Container>
auto findMax(const Container& container) -> typename std::remove_reference<decltype(*std::begin(container))>::type {
    if (std::begin(container) == std::end(container)) {
        throw std::runtime_error("빈 컨테이너에서 최댓값을 찾을 수 없습니다.");
    }
    
    auto maxElement = *std::begin(container);
    
    for (const auto& item : container) {
        if (item > maxElement) {
            maxElement = item;
        }
    }
    
    return maxElement;
}

// 배열에서 최솟값 찾기
template <typename Container>
auto findMin(const Container& container) -> typename std::remove_reference<decltype(*std::begin(container))>::type {
    if (std::begin(container) == std::end(container)) {
        throw std::runtime_error("빈 컨테이너에서 최솟값을 찾을 수 없습니다.");
    }
    
    auto minElement = *std::begin(container);
    
    for (const auto& item : container) {
        if (item < minElement) {
            minElement = item;
        }
    }
    
    return minElement;
}

// 배열 요소의 합계 계산
template <typename Container>
auto sum(const Container& container) -> typename std::remove_reference<decltype(*std::begin(container))>::type {
    if (std::begin(container) == std::end(container)) {
        return typename std::remove_reference<decltype(*std::begin(container))>::type();
    }
    
    auto total = *std::begin(container);
    auto it = std::begin(container);
    
    // 첫 번째 요소는 이미 처리했으므로 다음 요소부터 시작
    ++it;
    
    for (; it != std::end(container); ++it) {
        total += *it;
    }
    
    return total;
}

// 배열 요소의 평균 계산
template <typename Container>
double average(const Container& container) {
    if (std::begin(container) == std::end(container)) {
        throw std::runtime_error("빈 컨테이너에서 평균을 계산할 수 없습니다.");
    }
    
    // 합계 계산
    auto total = sum(container);
    
    // 요소 개수 계산
    size_t count = 0;
    for (auto it = std::begin(container); it != std::end(container); ++it) {
        ++count;
    }
    
    // 평균 반환
    return static_cast<double>(total) / count;
}

// 조건에 맞는 요소 개수 세기
template <typename Container, typename Predicate>
size_t countIf(const Container& container, Predicate pred) {
    size_t count = 0;
    
    for (const auto& item : container) {
        if (pred(item)) {
            ++count;
        }
    }
    
    return count;
}

// 조건에 맞는 요소로 새 컨테이너 만들기
template <typename Container, typename Predicate>
Container filter(const Container& container, Predicate pred) {
    Container result;
    
    for (const auto& item : container) {
        if (pred(item)) {
            result.insert(std::end(result), item);
        }
    }
    
    return result;
}

// 각 요소에 함수 적용하여 새 컨테이너 만들기
template <typename Container, typename Function>
auto map(const Container& container, Function func) -> Container {
    Container result;
    
    for (const auto& item : container) {
        result.insert(std::end(result), func(item));
    }
    
    return result;
}

int main() {
    // 벡터 테스트
    std::vector<int> numbers = {5, 2, 9, 1, 7, 6, 3, 8, 4};
    
    std::cout << "=== 벡터 테스트 ===" << std::endl;
    printAll(numbers, "원본 숫자");
    
    std::cout << "최댓값: " << findMax(numbers) << std::endl;
    std::cout << "최솟값: " << findMin(numbers) << std::endl;
    std::cout << "합계: " << sum(numbers) << std::endl;
    std::cout << "평균: " << average(numbers) << std::endl;
    
    // 5보다 큰 숫자 개수
    auto greaterThan5 = [](int n) { return n > 5; };
    std::cout << "5보다 큰 숫자 개수: " << countIf(numbers, greaterThan5) << std::endl;
    
    // 짝수만 필터링
    auto isEven = [](int n) { return n % 2 == 0; };
    std::vector<int> evenNumbers = filter(numbers, isEven);
    printAll(evenNumbers, "짝수만");
    
    // 각 숫자 제곱
    auto square = [](int n) { return n * n; };
    std::vector<int> squaredNumbers = map(numbers, square);
    printAll(squaredNumbers, "제곱 결과");
    
    // 리스트 테스트
    std::list<double> prices = {10.5, 25.3, 5.7, 35.9, 15.2};
    
    std::cout << "\n=== 리스트 테스트 ===" << std::endl;
    printAll(prices, "원본 가격");
    
    std::cout << "최고 가격: " << findMax(prices) << std::endl;
    std::cout << "최저 가격: " << findMin(prices) << std::endl;
    std::cout << "총 가격: " << sum(prices) << std::endl;
    std::cout << "평균 가격: " << average(prices) << std::endl;
    
    // 20불 이상인 가격 필터링
    auto over20 = [](double p) { return p >= 20.0; };
    std::list<double> highPrices = filter(prices, over20);
    printAll(highPrices, "20불 이상 가격");
    
    // 각 가격에 10% 세금 추가
    auto addTax = [](double p) { return p * 1.1; };
    std::list<double> pricesWithTax = map(prices, addTax);
    printAll(pricesWithTax, "세금 추가 가격");
    
    // 문자열 테스트
    std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David", "Eva"};
    
    std::cout << "\n=== 문자열 테스트 ===" << std::endl;
    printAll(names, "이름 목록");
    
    // 이름 길이가 5 이상인 것 필터링
    auto longName = [](const std::string& s) { return s.length() >= 5; };
    std::vector<std::string> longNames = filter(names, longName);
    printAll(longNames, "긴 이름 (5자 이상)");
    
    // 이름을 모두 대문자로 변환
    auto toUpper = [](std::string s) {
        for (char& c : s) {
            c = std::toupper(c);
        }
        return s;
    };
    std::vector<std::string> upperNames = map(names, toUpper);
    printAll(upperNames, "대문자 이름");
    
    return 0;
}

'Programming Languages > C++' 카테고리의 다른 글

STL 컨테이너  (0) 2025.03.28
STL (Standard Template Library)  (0) 2025.03.28
템플릿 메타프로그래밍  (0) 2025.03.28
가변 템플릿(Variadic Templates) (C++11)  (0) 2025.03.28
비타입 템플릿 매개변수  (0) 2025.03.28