티스토리 뷰

추가되는 기능

    • 객체지향 프로그래밍에서 상위 클래스의 상속을 이용하여 다음과 같은 기능을 구현할 수 있다.
      #include <iostream>
      
      class Base {
          public:
          virtual void foo() const {
              std::cout << "FOO" << std::endl;
          }
      };
      
      class Derived1 : public Base {
          public:
          void foo() const override {
              std::cout << "BAR1" << std::endl;
          }
      };
      
      class Derived2 : public Base {
          public:
          void foo() const override {
              std::cout << "BAR2" << std::endl;
          }
      };
      
      class Derived3 : public Base {
          public:
          void foo() const override {
              std::cout << "BAR3" << std::endl;
          }
      };
      
      class Derived4 : public Base {
          public:
          void foo() const override {
              std::cout << "BAR4" << std::endl;
          }
      };
      
      void do_foo(const Base& b) {
          b.foo();
      }
      
      int main() {
          Derived1 d1;
          Derived2 d2;
          Derived3 d3;
          Derived4 d4;
          
          do_foo(d1);
          do_foo(d2);
          do_foo(d3);
          do_foo(d4);
          
          return 0;
      }
      
      BAR1
      BAR2
      BAR3
      BAR4

      각 하위 클래스가 서로 다른 foo를 반드시 구현할 때 더 이상 상위 클래스에서의 foo의 정의는 필요하지 않다. 하지만 정의하지 않았을 때 하위 클래스가 반드시 정의한다는 보장을 할 수 없으므로 정의를 비워둘 수 없다.

    • 반드시 하위 클래스가 상위 클래스에 선언된 함수를 정의해야하고 상위 클래스에서 하위 클래스에서의 작동을 특정할 수 없을 때 순수 가상함수를 사용한다.
      #include <iostream>
      
      class Base {
          public:
          virtual void foo() const = 0;
      };
      

      이처럼 함수 정의 뒤에 '= 0'기호를 붙여주면 순수 가상함수의 선언이 된다. 순수 가상함수는 기능을 정의할 수 없으며 반드시 virtual 기호를 필요로 한다.

    • 순수 가상함수를 선언한 클래스는 객체화할 수 없다.

      #include <iostream> int main() { Base b; // error: cannot declare variable ‘b’ to be of abstract type ‘Base’ return 0; }

      이렇듯 컴파일 에러가 발생한다. 이유는 함수 foo가 정의되지 않아 인스턴스 생성시 함수 foo을 호출했을 때의 작동이 보장되지 않기 때문이다. 이렇게 pure virtual function을 선언한 클래스를 추상 클래스(abstract class)이라 칭한다.

    • 추상 클래스를 상속받은 모든 하위 클래스는 객체화하기 위해 반드시 순수 가상함수를 정의해야한다. 그렇지 않을 경우 그 클래스 또한 추상 클래스이다.
      #include <iostream>
      
      class Base {
          public:
          virtual void foo() const = 0;
      };
      
      class Derived : public Base {};
      
      int main() {
          Derived d; // error: cannot declare variable ‘d’ to be of abstract type ‘Derived’
          
          return 0;
      }
      

      추상 클래스를 상속받은 하위 클래스 역시 기능을 구체화할 수 없고 다른 클래스가 재상속받아 정의할 경우 그 재상속 받은 클래스만 객체화할 수 있다.

      #include <iostream>
      
      class Base {
          public:
          virtual void foo() const = 0;
      };
      
      class Derived : public Base {};
      
      class FarFarAway : public Derived {
          public:
          void foo() const override {
              std::cout << "foo" << std::endl;
          }
      };
      
      int main() {
          FarFarAway f; // OK
          
          return 0;
      }

목표

    • vector를 추상화한 class list를 정의한다.

디자인

    • class list를 추가한다.
    • list는 다음의 template parameter를 가진다.
      • typename T
    • list에 다음의 순수 가상함수를 추가한다.
      • void add(T element)
      • void add(int index, T element)
      • T get(int element) const
      • T set(int index, T element)
      • T remove(int index)
      • void print() const
      • T& operator(int index)
      • T operator(int index) = 0
    • vector는 list를 상속받는다.

구현

#include <iostream>
#include <algorithm>

template<typename T>
class list {
    public:
    virtual void add(T element) = 0;
    
    virtual void add(int index, T element) = 0;
    
    virtual T get(int index) const = 0;
    
    virtual T set(int index, T element) = 0;
    
    virtual T remove(int index) = 0;
    
    virtual void print() const = 0;
    
    virtual T& operator[](int index) = 0;
    
    virtual T operator[](int index) const = 0;
};

template<typename T, int INITIAL_SIZE = 10>
class vector : public list<T> {
    private:
    T* data;
    int capacity;
    int length;
    
    bool ensure_capacity(int to_add) const {
        return length + to_add < capacity;
    }
    
    void increase_capacity() {
        auto tmp = data;
        data = new T[capacity * 2];
        std::copy(tmp, tmp + length, data);
        delete[] tmp;
        capacity *= 2;
    }
    
    void shiftLeft(int offset, int width) {
        int tail_length = length - offset - width;
        auto tail = new T[tail_length];
        std::copy(data + offset + width, data + length, tail);
        std::copy(tail, tail + tail_length, data + offset);
        delete[] tail;
    }
    
    void shiftRight(int offset, int width) {
        int tail_length = length - offset;
        auto tail = new T[tail_length];
        std::copy(data + offset, data + length, tail);
        std::copy(tail, tail + tail_length, data + offset + width);
        delete[] tail;
    }
    
    protected:
    T* get_data() const {
        return data;
    }
    
    public:
    vector() {
        data = new T[INITIAL_SIZE];
        capacity = INITIAL_SIZE;
        length = 0;
    }
    
    vector(const vector& v) {
        data = new T[v.capacity];
        capacity = v.capacity;
        length = v.length;
        std::copy(v.data, v.data + v.length, data);
    }
    
    ~vector() {
        delete[] data;
    }
    
    void add(T element) override {
        if (!ensure_capacity(1))
            increase_capacity();
        
        *(data + length++) = element;
    }
    
    void add(int index, T element) override {
        if (!ensure_capacity(1))
            increase_capacity();
        
        shiftRight(index, 1);
        *(data + index) = element;
        length++;
    }
    
    T get(int index) const override {
        return *(data + index);
    }
    
    T set(int index, T element) override {
        auto tmp = *(data + index);
        *(data + index) = element;
        return tmp;
    }
    
    T remove(int index) override {
        auto tmp = *(data + index);
        shiftLeft(index, 1);
        length--;
        
        return tmp;
    }
    
    void print() const override {
        std::cout << '{';
        for (int i = 0; i < length; i++)
            std::cout << *(data + i) << ((i < length - 1) ? ", " : "");
        std::cout << '}' << std::endl;
    }
    
    vector& operator=(const vector& v) {
        data = new T[v.capacity];
        capacity = v.capacity;
        length = v.length;
        std::copy(v.data, v.data + capacity, data);
        
        return *this;
    }
    
    vector& operator+=(T element) {
        add(element);
        
        return *this;
    }
    
    T& operator[](int index) override {
        return *(data + index);
    }
    
    T operator[](int index) const override {
        return *(data + index);
    }
};

template<typename T>
void list_print(const list<T>& l) {
    l.print();
}

int main() {
    vector<int> v;
    v.add(10);
    v.add(20);
    v.add(30);
    v.add(40);
    v.add(50);
    list_print(v);
    
    return 0;
}


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

[C to C++] 상속 - 순수 가상함수, 추상 클래스  (0) 2019.02.03
[C to C++] 상속 - 생존주기, 가상함수  (0) 2019.02.02
[C to C++] 상속  (0) 2019.02.01
[C to C++] 템플릿  (0) 2019.02.01
[C to C++] friend, 연산자 오버로딩  (0) 2019.01.31
[C to C++] 함수 오버로딩  (0) 2019.01.31
댓글
댓글쓰기 폼