[C++] 클래스

Vaughan·2022년 7월 21일
0

C++

목록 보기
2/6
post-thumbnail

1. 구조체의 응용

01-구조체란?

C++에서 구조체는 클래스의 일종으로 간주되기 때문에, C에서와 달리 구조체 안에 함수를 정의할 수 있다.

  • 구조체는 서로 다른 자료형을 하나로 묶은 구조이다.
  • 서로 연관 있지만 자료형이 다른 데이터들을 하나로 묶을 수 있기 때문에 데이터 표현에 큰 도움을 준다.
  • 구조체 이용의 예시
    어떤 게임의 캐릭터를 표현할 때는 해당 캐릭터를 표현하기 위해 필요한 서로 연관있는 정보들이 모여서 표현될 것이다. (성별, 레벨, 경험치, 직업 …) 다른 캐릭터를 표현할 때도 앞의 정보들을 사용하므로 이를 이용해서 구조체를 정의하면 프로그래밍이 간단해진다.

02-구조체 변수의 정의 및 선언

  • 구조체 변수는 일반적인 변수와 달리, 먼저 사용하고자하는 구조의 구조체를 정의하고 그렇게 정의된 구조체 타입으로 변수를 선언하여 사용한다.
  • 구조체 정의 예시
    /* struct 구조체이름 {
    	   	자료형 멤버변수이름;
       		...
    }; */
    struct Student {
    		int number;
    		char name[10];
    		double height; 
    };
  • 구조체 선언 예시
    • C에서는 구조체 변수를 선언할 때, 앞에 struct 키워드를 붙여서 나타내었다. (생략을 위해 typedef선언을 구조체 정의시 추가함)
    • C++에서는 구조체 선언시 struct 키워드를 생략하여 변수를 선언할 수 있다.
    // 구조체이름 구조체변수이름
    Student vaughan;
    Student vaughan2 = {2022, vaughan, 175};  //선언과 동시에 멤버변수 초기화

03-구조체 안에 함수 정의하기

  • C에서는 구조체안에 일반적인 변수, 배열만을 멤버변수로 사용할 수 있었다.
    ⇒ 기존의 구조체에서 더 확장하여 구조체에 함수를 포함해보자.
  • 구조체에 종속적인 함수
    어떤 함수가 구조체에 대해서만 사용되는 종속적인 함수라면,
    • 전역함수가 아니라 구조체 안에 함수를 정의하여 해당 함수가 구조체에 종속적임을 나타낼 수 있다.
    • 구조체 안에서 함수가 정의되면 다른 영역에서 해당 함수를 호출하는 상황을 방지할 수 있을 것이다.
      func()로 함수 호출 → st.func()로 함수 호출
    • 함수가 구조체 내에 선언된 변수들에 대해 참조접근이 아니라 직접 접근이 가능해진다.
      st.namename
  • 구조체 안에 함수 정의 예시
    • ShowStudentInform() 함수는 구조체 내의 변수에 대한 정보를 출력한다. → 구조체 내에 정의되면서 참조접근이 아니라 직접 접근이 가능해짐
    • 이렇게 정의된 구조체로 구조체 변수를 선언하면, 각각의 변수가 모두 해당 구조체 함수를 공유하게된다.
    struct Student {
    		int number;
    		char name[10];
    		double height;
    		//함수 정의
    		void ShowStudentInform() {
    				cout<<"학번: "<<number<<endl;
    				cout<<"이름: "<<name<<endl;
    				cout<<"키: "<<height<<endl;
    		}
    };
    
    //전역함수로 정의된 경우
    void ShowStudentInform(Student st) {
    		cout<<"학번: "<<st.number<<endl;
    		cout<<"이름: "<<st.name<<endl;
    		cout<<"키: "<<st.height<<endl;
    }
  • 구조체 에 함수 정의 예시
    • 구조체 안에 함수를 정의하는 경우, 가독성을 위해 함수의 원형부분만을 구조체 안에 넣어두고 함수 정의 부분은 구조체 밖으로 빼낼 수 있다.
    • 구조체 밖에서 함수를 정의하는 경우 해당 함수가 구조체 내부에 정의됨을 나타내기 위해 함수의 이름 앞에 구조체타입의 이름을 기입한다. 구조체이름::
    struct Student {
    		int number;
    		char name[10];
    		double height;
    		//함수 원형
    		void ShowStudentInform();
    };
    
    //함수 정의
    void Student::ShowStudentInform() {
    		cout<<"학번: "<<number<<endl;
    		cout<<"이름: "<<name<<endl;
    		cout<<"키: "<<height<<endl;
    }
  • 구조체 함수와 인라인
    • 구조체 안에 함수가 정의되어있으면, 해당 함수를 인라인으로 처리하는 의미가 내포된다.
    • 따라서 위의 경우처럼 함수 정의를 구조체 밖으로 빼내면 해당 의미가 사라지기 때문에 인라인 처리를 유지하려면 키워드 inline을 이용하여 명시적으로 지시해야한다.

04-구조체 안에 enum 상수 정의하기

  • 상수 또한 어떤 구조체에게만 의미가 있는 종속적인 상수일 때, 구조체 내에 포함시킬 수 있다.
  • 구조체안에서만 유효한 상수를 정의할 때는 열거형 enum을 사용한다.
  • 구조체 안에 상수 정의 예시
    struct Student {
    		//상수 정의
    		enum {
    				NAME_LEN = 10,
    				SHOW = 5
    		};
    		
    		int number;
    		char name[NAME_LEN];
    		double height;
    		void ShowStudentInform() { ... }
    };

2. 클래스(class)

01-클래스와 구조체의 비교

구조체 선언에 사용하는 키워드 struct를 대신해 class를 사용하면 클래스를 정의할 수 있다. 그러나 이렇게 정의한 클래스를 선언하면 해당 클래스의 멤버 변수, 함수에 접근할 수 없다!

  • 클래스는 기본적으로 클래스 내부에 선언된 변수와 함수는 클래스 안에서만 접근할 수 있다.
  • 따라서 클래스는 각각의 멤버 변수 및 함수의 접근 허용범위를 별도로 명시해주어 클래스 밖의 다른 영역에서도 멤버에 접근할 수 있게 해주어야한다.

02-접근제어 지시자

  • 접근제어 지시자의 종류
    • public : 어떤 영역에서든 접근이 가능하다.
    • protected : 상속관계일 때, 유도 클래스에서 접근을 허용한다.
    • private : 클래스 내에서만 접근이 가능하다.
  • 접근제어 지시자의 특징
    • 한 접근제어 지시자가 선언되면 그 뒤에 정의된 변수, 함수는 해당 지시자의 접근범위 내에서 접근이 가능하다. → private 접근범위 : number, name
    • 이후 새로운 접근제어 지시자가 선언되면 그뒤에 정의된 변수, 함수는 새로운 접근제어 지시자의 접근범위 내에서 접근이 가능하다. → public 접근범위 : height, ShowStudentInform()
    • public 으로 선언된 멤버변수는 구조체의 멤버를 초기화하는 것처럼 초기화할 수 있다.
     class Student {
    		private:
    				int number;
    				char name[NAME_LEN];
    		public:
    				double height;
    				void ShowStudentInform() { ... }
    };
  • 클래스와 구조체, 접근제어 지시자
    • 클래스는 별도로 접근제어 지시자를 명시하지 않으면, 모든 변수와 함수는 private로 선언된다.
    • 구조체는 별도로 접근제어 지시자를 명시하지 않으면, 모든 변수와 함수는 public으로 선언된다.

03-C++에서 파일분할

  • C++은 특히 클래스 별로 헤더파일과 소스파일을 선언해 클래스의 선언과 정의를 분리하는 경우가 많기 때문에 하나의 프로그램에 많은 파일이 만들어진다.
  • 어떤 클래스 Student를 대상으로 파일 분할
    • Student.h : 클래스의 선언
    • Student.cpp : 클래스 멤버함수의 정의
  • 단, inline 키워드를 사용해 인라인 함수로 정의한 멤버함수는 클래스의 선언 부분에 해당되기 때문에 헤더파일에 넣어야한다.

3. 객체(Object)

01-객체지향 프로그래밍

  • 객체(Object)의 일반적인 의미
    • 물리적으로 존재하는 것 ex)자동차, 자전거, 책, 사람
    • 추상적인 대상중에서 자신의 속성을 가지고 있어 다른 것과 구분가능한 것 ex)학과, 강의
  • 객체는 하나이상의 상태 정보(데이터)와 하나 이상의 행동(기능)으로 구성된다.
  • 객체 모델링 (Object Modeling)
    • 현실 세계의 객체를 소프트웨어 객체로 설계하는 것
    • 현실 세계의 대상과 행동을 추려내어 소프트웨어 객체의 변수와 함수로 정의

💡 객체지향 프로그래밍은 현실에 존재하는 사물과 대상, 그리고 그에 따른 행동을 있는 그대로 실체화하여 부품 객체를 먼저 만들고 이것들을 하나씩 조립해 완성된 프로그램을 만드는 기법

02-클래스와 객체

  • 실제 존재하는 대상을 객체로 만들기 위해, 객체 생성을 위한 틀을 만들어야한다.
  • 이렇게 만들어진 틀이 바로 클래스(class)가 된다.
  • 동일한 클래스를 이용하여 비슷하게 정의되는 객체들을 찍어낼 수 있다.

03-클래스 기반의 객체생성 방법

  • 일반적인 변수의 선언방식
    //클래스이름 객체이름;
    Student vaughan;
  • 동적 할당방식
    //클래스이름* 객체이름 = new 클래스이름;
    Student* vaughan = new Student;

4. 정보은닉과 캡슐화

01-정보은닉(Information Hiding)

💡 멤버 변수에 대해 제한된 방법으로만 접근을 허용하여, 멤버변수가 가질 수 있는 값에 제한을 둘 수 있도록 프로그램을 설계하는 것이 좋다.

  • 클래스의 멤버변수가 public 일 때 발생할 수 있는 문제점
    • 프로그램에서 사용자가 직접 멤버에 접근하여 값을 지정할 때, 해당 멤버변수가 가질 수 있는 값에 대한 조건이 필요하다고 가정하자. 이러한 상황에서 사용자가 해당 멤버변수의 값을 조건을 만족하지 않는 값으로 초기화 하더라도 문법적으로는 문제가 없기 때문에 이런 실수를 발견하지 못한다.
    • 예시) 어떤 도형의 넓이를 나타내는 멤버변수의 값은 양수만을 가질 수 있다.
  • 정보은닉방법
    • 멤버변수를 private로 선언하여 임의로 값을 지정하는 방법을 막아둔다. ⇒ 정보를 은닉한 상태
    • 대신 해당 멤버변수에 접근하고, 멤버변수의 값을 변경하기위한 함수를 추가로 정의한다.
    • 정의한 함수를 사용해서 숨겨진 멤버변수에 접근하여 어떤 특정 조건을 만족하는 값만으로 초기화 될 수 있게 제한할 수 있다.
  • 엑세스 함수(access function)
    • private로 선언한 멤버변수를 클래스 외부에서 접근하기 위해 정의되는 함수
    • 주로 Get__, Set__ 으로 함수의 이름이 정의된다.
    • 정의되었으나 실제로 호출되지 않는 경우도 있다.
  • 정보은닉을 활용한 클래스 예시
    • Point 클래스는 양의 좌표상의 x값, y값으로 나타난다.
    • private 멤버변수에 값을 저장하기 위해 InitMembers(), setX(), setY()함수를 선언했다.
      • 주어진 조건을 만족하는 값이 전달되었을 때 저장한다.
      • 조건을 만족하지 않는 잘못된 값이 전달되면 에러메시지를 출력해 문제가 있음을 확인할 수 있다.
    • private 멤버변수에 접근하기 위해 getX(), getY()함수를 선언한다.
    #include <iostream>
    using namespace std;
    
    class Point {
        private:
            int x;
            int y;
    	public :
            int GetX() const;
            int GetY() const;
            bool InitMembers(int xpos, int ypos);
            bool SetX(int xpos);
            bool SetY(int ypos);
    };
    
    bool Point::InitMembers(int xpos, int ypos) {
        if(xpos<0 || ypos<0){
            cout<<"잘못된 범위의 값이 전달됨"<<endl;
            return false;
        }
        x = xpos;
        y = ypos;
        return true;
    }
    bool Point::SetX(int xpos) {
        if(xpos<0){
            cout<<"잘못된 범위의 값이 전달됨"<<endl;
            return false;
        }
        x = xpos;
        return true;
    }
    bool Point::SetY(int ypos) {
        if(ypos<0){
            cout<<"잘못된 범위의 값이 전달됨"<<endl;
            return false;
        }
        y = ypos;
        return true;
    }
    int Point::GetX() const {return x;}
    int Point::GetY() const {return y;}

02-const 함수

  • const 함수는 함수 선언 뒤에 const 선언이 추가되어있는 함수를 의미한다. ex) int GetX() const;
  • const 함수의 의미
    • 해당 함수 안에서는 멤버변수에 저장된 값을 변경하지 않는다는 의미를 명시
    • 실수로 const 함수 안에서 멤버변수의 값을 변경하면 컴파일 에러가 발생하게된다.
    • const 함수는 멤버변수의 값을 변경할 수 있는 능력을 지닌 const 함수가 아닌 일반 함수를 호출할 수 없게 막는다.

03-캡슐화(Encapsulation)

  • 캡슐화의 의미와 필요성
    • 캡슐화는 둘 이상의 기능을 모아 하나의 목적을 달성하도록 하는 것
    • 또한 캡슐화는 해당 클래스의 사용에 있어 진행순서가 중요한 경우에 각 기능을 절차적으로 실행하는 과정을 명시하여 간결하게 나타낼 수 있다.
  • 캡슐화의 예
    • 예시) 감기약은 재채기, 콧물, 코막힘등의 증상을 완화시켜 감기를 낫게하는 목적을 달성한다.
    • 예시 코드
        #include <iostream>
        using namespace std;
        
        class SneezeCap { public: void Take() const { cout<<"재채기 증상 완화약 처방"<<endl; } };     //재채기 약
        class SinivelCap { public: void Take() const { cout<<"콧물 증상 완화약 처방"<<endl; } };     //콧물 약
        class SnufflelCap { public: void Take() const { cout<<"코막힘 증상 완화약 처방"<<endl; } };  //코막힘 약
        
        //각 증상을 완화하는 기능을 모아 감기를 낫게하는 목적을 달성하는 하나의 캡슐
        class CAP {
            private:
                //증상 완화를 위한 약 객체
                SneezeCap sne;
                SinivelCap sin;
                SnufflelCap snu;
            public:
                //약의 복용 절차를 나타내는 복용 함수
                void Take() const{
                    sne.Take();
                    sin.Take();
                    snu.Take();
                }
        };
        
        //감기환자
        class ColdPatient {
            //감기증상 완화를 위해 캡슐을 처방하는 함수. (캡슐 객체를 인자로 전달받음) 
            public:
                void TakeCAP(CAP &cap) const { cap.Take(); }
        };
        
        int main(){
            ColdPatient patient;
            CAP cap;
            patient.TakeCAP(cap);
            return 0;
        }

실행 결과

  • 캡슐화와 정보은닉
    • 캡슐화는 여러 기능을 하나로 감싸는 개념이다.
    • 감싸기 위해서는 정보를 은닉하여 안전하게 감싸는 것이 중요하기 때문에, 캡슐화는 기본적으로 정보은닉을 포함하는 개념이다.

5. 생성자와 소멸자

01-생성자의 사용과 특징

객체 생성시 반드시 호출되는 것

  • 생성자의 정의
    생성자(constructor)는 클래스 내부에 정의된 함수 중에서 다음의 형태를 띄는 함수를 지칭한다.
    • 클래스의 이름과 동일한 이름의 함수
    • 반환형이 없으며, 실제로도 반환하는 값이 없는 함수
  • 생성자의 특징
    • 객체를 생성할 때, 단 한번만 호출된다.
    • 생성자도 함수의 일종이기 때문에
      • 오버로딩이 가능하다.
      • 매개변수에 디폴트 값을 설정할 수 있다.
  • 생성자를 이용한 객체생성방법
    • 전역 및 지역변수의 형태
      클래스명 객체명(생성자인자);
    • 동적 할당의 형태
      클래스명* 객체명 = new 클래스명(생성자인자);
  • 생성자를 사용한 클래스 예시
    #include <iostream>
    using namespace std;
    
    class Student {
    	private:
    		int number;
    		float grade;
    	public:
        //생성자 정의
    		Student(){
            number = 0;
            grade = 0;
        }
        Student(int num){
            number = num;
            grade = 0;
        }
        Student(int num, float g){
            number = num;
            grade = g;
        }
        void Show(){
            cout<<"학번: "<<number<<endl;
            cout<<"학점: "<<grade<<endl<<endl;
        }
    };
    
    int main(){
        Student st1;             //기본 생성방법 (==인자가 없는 기본 생성자 1)
        Student st2(2021);       //생성자 2
        Student st3(2022, 4.5);  //생성자 3
    
        st1.Show();
        st2.Show();
        st3.Show();
        return 0;
    }

실행결과

  • Student st1() 으로 객체 생성을 할 수 없는이유
    위 명령문은 다음의 형태를 갖춘 함수의 원형 선언문과 구분할 수 없기 때문에 인자가 없는 생성자를 이용해 객체를 생성할 때는 ()를 쓰지 않는다.
    • 반환형이 Student 클래스 객체
    • 인자를 가지지 않음
    • 함수 이름은 st1

02-멤버 이니셜라이저(Member Initializer)

  • 멤버 이니셜라이저의 필요성
    • 클래스가 객체를 멤버변수로 가질 때, 클래스를 생성하면서 동시에 멤버 객체의 초기화를 하려고 한다면 어떻게 해야하는가?
    • 멤버 이니셜라이저는 멤버변수로 선언된 객체의 생성자 호출에 사용된다.

      💡 멤버 객체의 생성자 호출을 클래스의 생성자 호출과 동시에 진행하여 클래스 객체 생성과 동시에 클래스가 갖는 멤버 객체의 초기화를 가능하게 만들어준다.

  • 멤버 이니셜라이저의 사용방법
    • 멤버 이니셜라이저는 생성자 정의부분의 인자 다음에 위치한다.
    • 생성자의 인자로 전달되는 값을 이용하여 멤버 객체의 생성자를 호출하도록 한다.
    • 멤버 이니셜라이저는 이미 클래스 안에 멤버변수로 선언된 변수/객체에 대해서만 사용할 수 있다.
  • 이니셜 라이저의 특징
    • 객체 뿐만 아니라 일반적인 멤버변수의 초기화에도 사용할 수 있다.
    • 이니셜라이저를 통해 변수를 초기화하면, 변수의 생성과 동시에 초기화하는 바이너르 코드를 구성한다.
      : num(n) 의 의미는 int num = n 이다.
    • const 멤버변수, 참조자도 이니셜라이저를 이용하여 초기화할 수 있다.
      • const 변수 : 선언과 동시에 초기화 필요
      • 참조자 : 선언과 동시에 초기화 필요
  • 멤버 이니셜라이저 사용예시
    • 다음 클래스는 점의 좌표를 나타내는 Point 클래스를 이용해 좌상단 점과 우하단 점을 갖는 직사각형을 나타낸다.
    • Rectangle의 생성자에 멤버 이니셜라이저를 사용하여 인자로 전달된 좌표를 갖는 Point 객체를 만들 수 있게 했다.
      : upLeft(x1, y1), lowRight(x2, y2)
    #include <iostream>
    using namespace std;
    
    class Point {
        private:
            int x;
            int y;
    	  public :
            Point(const int &xpos, const int &ypos); //생성자
            int GetX() const;
            int GetY() const;
            void SetX(int xpos);
            void SetY(int ypos);
    };
    Point::Point(const int &xpos, const int &ypos) {
        x = xpos;
        y = ypos;
    }
    int Point::GetX() const { return x; }
    int Point::GetY() const { return y; }
    void Point::SetX(int xpos) { x = xpos; }
    void Point::SetY(int ypos) { y = ypos; }
    
    class Rectangle {
        private:
            Point upLeft;
            Point lowRight;
        public:
            Rectangle(const int &x1, const int &y1, const int &x2, const int &y2);
            void ShowRec() const;
    };
    Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2) 
    : upLeft(x1, y1), lowRight(x2, y2) {
    }
    void Rectangle::ShowRec() const {
        cout<<"좌상단: "<<'['<<upLeft.GetX()<<", "<<upLeft.GetY()<<']'<<endl;
        cout<<"우하단: "<<'['<<lowRight.GetX()<<", "<<lowRight.GetY()<<']'<<endl;
    }
    
    int main(){
        Rectangle rec(1, 1, 4, 4);
        rec.ShowRec();
        return 0;
    }

실행 결과

03-*객체의 생성과정

  1. 메모리 공간의 할당
  2. 이니셜라이저를 이용한 멤버변수 객체의 초기화 (없으면 생략)
  3. 생성자의 body부분 실행

04-생성자의 종류

  • 디폴트 생성자
    • 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야한다.
    • 따라서 사용자가 생성자를 정의하지 않은 클래스에는 컴파일러에 의해 자동으로 디폴트 생성자가 삽입된다.
    • 디폴트 생성자의 구조
      • 인자를 받지 않으며, 생성자 내부(body)에서 어떤 초기화도 진행하지 않는다.
      • 따라서 디폴트 생성자는 다음의 생성자와 동일한 기능을 한다.
        클래스명(){}
    • 생성자 불일치
      • 디폴트 생성자는 사용자가 정의한 생성자가 1개라도 존재할시 삽입되지 않는다.
      • 따라서 인자를 받는 생성자를 만든 뒤에는 클래스명 객체명; 의 방식으로 객체를 생성할 수 없다.
  • private 생성자
    • 대부분의 생성자는 public으로 선언된다.
    • private로 선언된 생성자는 클래스 내부에서 그 클래스의 객체를 생성할 때 사용할 수 있다.
    • private 생성자 사용 예시
        #include <iostream>
        using namespace std;
        
        class SimpleClass {
            private:
                int num;
                SimpleClass(int n) : num(n) {}   //private 생성자
            public:
                SimpleClass(){}
                void SetNum(int i) { num = i; }
                SimpleClass& CreateClass(int n) const{
                    SimpleClass* ptr = new SimpleClass(n);
                    return *ptr;
                }
                void Show() const { cout<<"숫자: "<<num<<endl; }
        };
        
        int main(){
            SimpleClass out;
            out.SetNum(100);
            SimpleClass in = out.CreateClass(10);
            SimpleClass inin = in.CreateClass(1);
            out.Show();
            in.Show();
            inin.Show();
            
            return 0;
        }

실행 결과

05-소멸자의 사용

객체 소멸시 반드시 호출되는 것

  • 소멸자의 형태
    • 클래스의 이름앞에 ‘~’가 붙은 형태의 이름을 가진다.
    • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
    • 매개변수는 void형으로 반드시 선언되므로 생성자와 달리 오버로딩, 디폴트 값 설정을 할 수 없다.
  • 소멸자의 특징
    • 생성자에서 할당한 리소스의 소멸에 사용된다.
      delete를 이용해 할당해둔 메모리 공간을 소멸한다.
    • 생성자처럼 객체 소멸시 반드시 호출되기 때문에 사용자가 직접 소멸자를 정의하지 않으면 아무런 일도 하지 않는 디폴트 소멸자가 컴파일러에 의해 자동으로 삽입된다.
    • 사용자가 호출하지 않아도 객체가 소멸될 때 자동으로 호출된다.
  • 소멸자 사용예시
    #include <iostream>
    using namespace std;
    
    class Person {
        private:
            char* name;
            int age;
        public:
            Person(char* str, int n): age(n) {
                int len = strlen(str) + 1;
                name = new char[len];
                strcpy(name, str);
            }
            void Show() const { 
                cout<<"나이: "<<age<<endl; 
                cout<<"이름: "<<name<<endl; 
            }
            //소멸자
            ~Person() {
                delete []name;  //동적할당한 멤버를 소멸
                cout<<"소멸자 실행됨"<<endl;
            }
    };
    
    int main(){
        Person cls("vaughan", 21);
        cls.Show();
        return 0;
    }

실행결과


6. 클래스의 배열 활용

01-객체 배열

  • 객체 배열의 선언
    • 기본 선언방법
      클래스명 배열이름[크기]
    • 동적할당
      클래스명* 배열이름 = new 클래스명[크기];
  • 객체 배열의 특징
    • 객체 배열은 해당 크기만큼의 객체가 모여 배열을 구성하는 형태가 된다.
    • 이렇게 객체 배열을 선언할 때도 객체의 생성자가 호출된다. (단, 이때는 호출하는 생성자는 인자를 전달하지 못하는 생성자이다.)
    • 따라서 객체배열을 생성하려면 클래스명() { //body } 형태의 생성자가 정의되어 있어야 한다.
    • 생성한 각각의 객체를 초기화시키려면 별도의 함수를 이용한 초기화 과정을 거쳐야한다.
      → 이미 배열에 생성되면서 그 배열을 이루는 객체도 생성되어있는 상태이기 때문에 생성자를 이용하여 초기화할 수 없고, Set함수 등을 정의하여 이를 이용하여 초기화해야한다.
  • 객체배열 사용예시
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class Person {
        private:
            char* name;
            int age;
        public:
            //객체배열 생성을 위한 () 생성자
            Person(){ 
                name = NULL;
                age = 0;
                cout<<"기본 생성자 호출"<<endl; 
            }
            void SetInfo(char* name, int age) {
                this->name = name;
                this->age = age;
            }
            void Show() const {  
                cout<<"이름: "<<name<<endl;
                cout<<"나이: "<<age<<endl;
            }
    };
    
    int main(){
        Person arr[3];      //객체 배열
        char namestr[10];
        int age;
        int len;
        char* strptr;
        //객체 초기화
        for(int i=0; i<3 ; i++){
            cout<<"이름을 입력하시오: ";
            cin>>namestr;
            cout<<"나이를 입력하시오: ";
            cin>>age;
    
            len = strlen(namestr) + 1;
            strptr = new char[len];
            strcpy(strptr, namestr);
            arr[i].SetInfo(strptr, age);
        }
        //저장된 객체 배열 출력
        for(int i=0; i<3; i++){ arr[i].Show(); }
        return 0;
    }

실행 결과

02-객체 포인터 배열 💫

  • 객체 포인터 배열
    • 객체의 주소값을 저장하는 포인터로 이루어진 배열
    • 포인터이기 때문에 객체타입의 포인터로 배열을 선언한다.
      클래스명* 배열이름[크기]
    • 객체 포인터 배열은 각 포인터가 가리킬 객체를 생성하고, 생성한 객체의 주소를 포인터 배열에 대입하는 방식으로 초기화할 수 있다.
      → 객체 배열과 달리 객체의 인자전달 생성자를 사용하여 초기화 할 수 있다.
  • 객체 포인터 배열 사용예시
    (객체 포인터 배열을 사용했을 뿐, 수행하는 기능은 01-객체배열의 예시와 동일하다)
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class Person {
        private:
            char* name;
            int age;
        public:
            Person(char* name, int age){ 
                int len = strlen(name) + 1;
                this->name = new char[len];
                strcpy(this->name, name);
                this->age = age;
                cout<<"인자 생성자 호출"<<endl; 
            }
            void SetInfo(char* name, int age) {
                this->name = name;
              this->age = age;
            }
            void Show() const {  
                cout<<"이름: "<<name<<endl;
                cout<<"나이: "<<age<<endl;
            }
    };
    
    int main(){
        Person* arr[3];      //객체 배열
        char namestr[10];
        int age;
    
        //객체를 생성한뒤, 객체 포인터에 그 주소를 대입하여 초기화
        for(int i=0; i<3 ; i++){
            cout<<"이름을 입력하시오: ";
            cin>>namestr;
            cout<<"나이를 입력하시오: ";
            cin>>age;
    
            arr[i] = new Person(namestr, age);
        }
        //저장된 객체 배열 출력
        for(int i=0; i<3; i++){ arr[i]->Show(); }
        return 0;
    }

수행결과

객체를 배열형태로 저장할 때는 배열에 저장되는 대상을 ‘객체'로 하는지[객체 배열], ‘객체의 주소'로 하는지[객체 포인터 배열]에 따라 다른 방법으로 배열을 선언해야한다.

03-this 포인터와 Self-참조자

  • this 포인터의 의미
    • 객체 자기자신의 주소값을 의미한다.
    • this 포인터는 주소값과 자료형이 정해져 있지 않고, 자신이 사용되고 있는 객체에 대한 정보를 자동으로 갖게 된다.
  • this 포인터의 활용
    • 멤버변수와 매개변수의 이름이 동일할 때, 멤버변수를 나타내기위해 사용된다.
      this→num = num;
    • 유사한 성격을 띠는 변수의 이름을 짓기 힘들때 this 포인터를 사용하면 더 간단하게 만들 수 있다.
    • *주의 : JAVA나 python에서는 this를 바로 접근하여 사용하지만, (this.) C++에서 this는 포인터이기 때문에, 참조한 뒤에 접근((*this). or this→)하게 된다.
  • Self-Reference
    • 객체 자기자신을 참조할 수 있는 참조자
    • this포인터를 이용해 Self-Reference를 반환할 수 있다.
    • Self-Reference 반환 예시
          #include <iostream>
          using namespace std;
          
          class ClassEx {
              private:
                  int num;
              public:
                  ClassEx(int num) {
                      this->num = num;
                      cout<<"생성자 호출"<<endl;   
                  }
                  ClassEx**&** Add(int num) {
                      this->num += num; //숫자를 더함
                      return *this;    //자기자신 객체를 반환
                  }
                  ClassEx**&** Show() {
                      cout<<num<<endl; //숫자를 출력
                      return *this;    //자기자신 객체를 반환
                  }
          };
          
          int main(){
              ClassEx cls(10);
              ClassEx **&ref** = cls.Add(5);  //반환된 객체참조자를 참조자에 저장
          
              cls.Show();    //15
              ref.Show();    //15
              ref.Add(5).Show().Add(5).Show();  //20, 25
              cls.Show();    //25
          
              return 0;
          }

본문은 ⟪열혈 C++ 프로그래밍, 윤성우⟫ 도서에 기반하여 정리한 내용입니다.

profile
우주의 아름다움도 다양한 지식을 접하며 스스로의 생각이 짜여나갈 때 불현듯 나를 덮쳐오리라.

0개의 댓글