본문 바로가기

C++

c++ 14 ( Generic 이라고도 불릴 수 있는 개념 )

STL

Standard Template Library의 약자

STL을 사용하면 각종 자료형에 구애받지 않는

통합 라이브러리 (유용하게 활용가능한 소스코드나 모듈)을 만들 수 있으며

C++의 STL은 다른 언어 Generic과는 다르게 굉장히 빠른 실행 퍼포먼스를 약속한다.

 

 

template <typername T>

STL 클래스에서 타입을 나중에 지정하겠다고 명시하는 방법

여기에서 이 T가 나중에 지정할 타입이름이 된다

 

안에 있는 내부의 클래스의 어떤 타입을 나중에 이것의 인스턴스를 만들 때 지정할 수 있도록 선언

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>


template <typename T>
// 이 클래스 내부에는 타입이 지정되지 않은 어떤 것이 있다, 타입의 지정을 뒤로 미룰 수 있다


class MyClass {
public:
    T value;
};


template <typename T>
void swap(T* a, T* b){ // 모든 타입에서 가능한 swap
    T tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}


int main() {
    MyClass<int>* m = new MyClass<int>();
    // value는 이 인스턴스에서는 int다
    m->value = 100;
    
    MyClass<std::string>* ms = new MyClass<std::string>();
    // MyClass에 std string을 typename으로 지정하고 포인터로 선언
    ms->value = "Hello World";
    // 여기서부터 ms의 value는 문자열
    
    
    int value1 = 100;
    int value2 = 200;
    
    swap<int>(&value1, &value2);
    // swap이라는 함수는 T가 int라는 타입으로 변환되는것
    
    printf("value1 : %d\n", value1);
    printf("value2 : %d\n", value2);
    
    
    /*
     std::string에서도 대응 가능
     std::string value1 = 100;
     std::string value2 = 200;
     
     swap<std::string>(&value1, &value2);
     
     printf("value1 : %d\n", value1.c_str());
     printf("value2 : %d\n", value2,c_str());
     */
    
    return 0;
}

 

 

 

STL - 가변 배열 클래스 작성

STL의 방법을 활용하여 가변 배열 클래스를 작성한다.

이를 통해 코드의 재사용성을 늘리는

필요할때마다 그때그때 모든 코드를 하나하나 만드는 것이 아닌

하나의 통합적인 자료관리 모델을 만들고

이를 활용하는 코드를 생산해본다.

 

고정배열은 크기를 지정해놓고 나중에 그 크기를 변경을 할 수가 없다.

가변배열을 template 클래스로 만들어보기

 

최초의 MyArray라는 클래스를 선언하게 되면 이 MyArray라는 클래스에서 

arrayItem을 동적할당으로 만들어주게 된다.

그 동적할당으로 만들어진 크기는 capacity, 처음에는 8

 

어떤 값이 카운트를 통해 배열로 추가가 되는데 처음에는 8

그 8을 넘어간다면?? 그러면 배열 포인터를 다시 초기화하여 크기를 더 크게 만들어주기

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>


template <typename T>
class MyArray {
public:
    T* arrayItem;
     // 배열포인터
     // arrayItem이라고 하는 변수는 뭔지 모르겠지만 나중에 type지정해주고 배열 포인터로 활용
    
    int count = 0;
     // 배열아이템에 값을 추가를 해 주려면 arrayItem에 몇개의 배열 원소가 있는지 카운트
    
    int capacity = 8; // 배열이 최대로 가질 수 있는 크기
    
    MyArray() { // T* arrayItem; 처음에 고정배열, 생성자에서 초기화 해주기
        arrayItem = new T[capacity];
    }
    
    virtual ~MyArray() { // 소멸자
        delete[] arrayItem; // 메모리해제
    }
    
    void putValue(T value) { // arrayItem에 값을 집어넣을때 사용
        if (capacity > count) {
            arrayItem[count] = value;
            count++;
        }
        
        else { // capacity는 1부터 시작 count는 0부터 시작
            
            printf("배열의 캐파시티가 두 배로 늘어났습니다\n");
            
            T* newarray = new T[capacity * 2];
            capacity = capacity * 2; // capacity가 올라간 만큼에서 다시 배열 채워지게
            // 어떤 값이 카운트를 통해 배열로 추가가 되는데 처음에는 8
            //그 8을 넘어간다면?? 그러면 배열 포인터를 다시 초기화하여 크기를 더 크게 만들어주기
            
            for (int i = 0. ; i < count; i++) {
                newarray[i] = arrayItem[i];
                // 기존의 배열값을 복사해서 새로운 배열에 할당을 해주고
                // 그 다음 새로 들어오는 putValue(T value) 값을 집어넣기
            }
            delete [] arrayItem; // 기존 arrayItem free해주기
            arrayItem = newarray; // newarray를 2배를 가진 새로운 arrayItem에 넣어주기
            arrayItem[count] = value;
            count++;
        }
    }
};


int main() {
    
    MyArray<int> m = MyArray<int>();
    // MyArray 정적으로 만들기
    
    m.putValue(100);
    m.putValue(200);
    m.putValue(300);
    m.putValue(400);
    m.putValue(500);
    m.putValue(600);
    m.putValue(700);
    m.putValue(800);
    m.putValue(900);
    
    for (int i = 0; i < m.count; i++) {
        printf(" %d ", m.arrayItem[i]);
    }
    
    return 0;
}

 

위 코드 깔끔하게 정리

(for문)

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>


template <typename T>
class MyArray {
private: // arrayItem과 capacity를 이 클래스를 사용하는 사람이 보지못하도록 막는다.
    T* arrayItem;
    int count = 0;
    int capacity = 8;


public:
    MyArray() { // 생성자
        arrayItem = new T[capacity];
    }
    
    virtual ~MyArray() { // 소멸자
        delete[] arrayItem; // 메모리해제
    }
    
    void putValue(T value) { // arrayItem에 값을 집어넣을때 사용
        if (capacity <= count) {
            
            printf("배열의 캐파시티가 두 배로 늘어났습니다\n");
            
            T* newarray = new T[capacity * 2];
            capacity = capacity * 2;
        
            for (int i = 0. ; i < count; i++) {
                newarray[i] = arrayItem[i];
                
            }
            delete [] arrayItem; // 기존 arrayItem free해주기
            arrayItem = newarray; // newarray를 2배를 가진 새로운 arrayItem에 넣어주기
        }
    
        arrayItem[count] = value;
        count++;
    }
    
    int getCount() {
        return count; // count숫자 리턴
    }
    
    T getValue(int index) {
        // index위치에 있는 array배열 변수의 값을 리턴
        return arrayItem[index];
    }
};


int main() {
    
    MyArray<int> m = MyArray<int>();


    m.putValue(100);
    m.putValue(200);
    m.putValue(300);
    m.putValue(400);
    m.putValue(500);
    m.putValue(600);
    m.putValue(700);
    m.putValue(800);
    m.putValue(900);
    
    for (int i = 0; i < m.getCount(); i++) {
        printf(" %d ", m.getValue(i));
    }
    
    return 0;
}

 

 

문자열인 경우

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>


template <typename T>
class MyArray {
private: // arrayItem과 capacity를 이 클래스를 사용하는 사람이 보지못하도록 막는다.
    T* arrayItem;
    int count = 0;
    int capacity = 8;


public:
    MyArray() { // 생성자
        arrayItem = new T[capacity];
    }
    
    virtual ~MyArray() { // 소멸자
        delete[] arrayItem; // 메모리해제
    }
    
    void putValue(const T& value) { // 레퍼런스로 받아오면 값복사 시간이 줄어든다
        // const로 받아오게 되면 이 변할 수 없는 상수형의 문자열의 레퍼런스라는 의미가 된다
        if (capacity <= count) {
            
            printf("배열의 캐파시티가 두 배로 늘어났습니다\n");
            
            T* newarray = new T[capacity * 2];
            capacity = capacity * 2;
        
            for (int i = 0. ; i < count; i++) {
                newarray[i] = arrayItem[i];
                
            }
            delete [] arrayItem; // 기존 arrayItem free해주기
            arrayItem = newarray; // newarray를 2배를 가진 새로운 arrayItem에 넣어주기
        }
    
        arrayItem[count] = value;
        count++;
    }
    
    int getCount() {
        return count; // count숫자 리턴
    }
    
    T getValue(int index) {
        // index위치에 있는 array배열 변수의 값을 리턴
        return arrayItem[index];
    }
};


int main() {
    
    MyArray<std::string> m = MyArray<std::string>();


    m.putValue("a");
    m.putValue("b");
    m.putValue("c");
    m.putValue("d");
    m.putValue("e");
    m.putValue("f");
    m.putValue("g");
    m.putValue("h");
    m.putValue("i");
    
    for (int i = 0; i < m.getCount(); i++) {
        printf(" %s ", m.getValue(i).c_str());
    }
    
    return 0;
}

 

 

 

STL 선언과 정의 분리

STL은 선언과 정의를 분리할 수 없다.

이유는 C++ 컴파일러가 그때그때 필요한 타입에 맞는 STL클래스를 가져와서

그것을 이용해 STL 클래스 몸체를 만들거나

STL 함수를 만들기 때문이다.

마치 전처리기 (매크로) 와 같은 동작을 하는 것이 STL이기 때문이다.

 

때문에 STL 클래스는 보통 헤더파일에만 존재하게 된다.

 

 

 

과제형 연습 프로그래밍

 

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>


template <typename T>
class MyArray {
private: // arrayItem과 capacity를 이 클래스를 사용하는 사람이 보지못하도록 막는다.
    T* arrayItem;
    int count = 0;
    int capacity = 8;


public:
    MyArray() { // 생성자
        arrayItem = new T[capacity];
    }
    
    virtual ~MyArray() { // 소멸자
        delete[] arrayItem; // 메모리해제
    }
    
    void putValue(const T& value) { //레퍼런스로 받아오면 값복사 시간이 줄어든다
        // const로 받아오게 되면 이 변할 수 없는 상수형의 문자열의 레퍼런스라는 의미가 된다
        if (capacity <= count) {
            
            printf("배열의 캐파시티가 두 배로 늘어났습니다\n");
            
            T* newarray = new T[capacity * 2];
            capacity = capacity * 2;
        
            for (int i = 0. ; i < count; i++) {
                newarray[i] = arrayItem[i];
                
            }
            delete [] arrayItem; // 기존 arrayItem free해주기
            arrayItem = newarray; // newarray를 2배를 가진 새로운 arrayItem에 넣어주기
        }
    
        arrayItem[count] = value;
        count++;
    }
    
    int getCount() {
        return count; // count숫자 리턴
    }
    
    T getValue(int index) {
        // index위치에 있는 array배열 변수의 값을 리턴
        return arrayItem[index];
    }
    
    // value 값이 존재하는지 확인하는 함수
    bool contains(T value) {
        for(int i = 0; i < count; i++) {
            if (value == arrayItem[i]) {
                return true;
            }
        }
        return false;
    }
    
    // 값을 변경
    void replace(int index, T value) {
        if (index < count) {
            arrayItem[index] = value;
        }
    }
    
    // 값을 삭제 (단, 빈 공간을 채워줘야 함)
    void erase(int index) {
        for(int i = index; i < count-1; i++) { // count까지 가면 안된다.
            // i+1번째에서 i번째에 값을 하나씩 넣어주면 된다.
            arrayItem[i] = arrayItem[i + 1];
        }
        count--; // 카운트를 1줄인다.
    }
};


int main() {
    
    MyArray<int> m = MyArray<int>();


    m.putValue(10);
    m.putValue(11);
    m.putValue(15);
    m.putValue(20);
    
    //m.erase(0);
    //m.replace(2, 100);
    
    if (m.contains(11)){
        printf("값을 포함하고 있습니다.\n");
    }
    else {
        printf("포함하지 않습니다.\n");
    }
    
    for (int i = 0; i < m.getCount(); i++) {
        printf(" %d ", m.getValue(i));
    }
    
    return 0;
}