본문 바로가기

C++

c++ 3 (클래스 상속 / 접근지정자)

상속 (Inheritance)

어떤 상위 개념을 가지고 더 디테일한 하위 개념을 만드는 것

-> 어떤 개념을 확장해 새로운 개념을 만들어내는 것, 추상화된 데이터들을 이용해 더 복잡한 개념을 만들어내는 것

 

예시를 들었던 프로토스 유닛이라는 개념을 확장해서 질럿 이라는 세부개념을 만들어내는 것

클래스를 상속하게 되면 상속하는 클래스와 상속받는 클래스가 존재

 

  • 상속 하는 클래스부모 클래스
  • 상속 받는 클래스자식 클래스

 

상속방법

class ChildClass : public ParentClass {

 ...

}

class 자식클래스 : public 부모클래스 { ... }

 

상속을 받을 클래스의 오른쪽에 콜론( : )public 과 함께 부모클래스명을 기입하면 상속이 이루어진다.

 

멤버변수와 멤버함수

자식클래스는 부모클래스의 상속받은 모든 멤버변수와 멤버함수를 활용할 수 있으며 외부에서도 활용 가능

 

생성자의 실행순서

자식클래스의 생성자를 호출하게 되면 자식클래스는 기본적으로 부모 클래스의 '기본 생성자'를 호출하게 된다

 자식클래스의 생성자가 호출되기 직전 자동으로 호출되며

부모클래스의 생성자가 호출된 이후 자식클래스의 생성자가 호출된다

 

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>


class Animal {
public:
    int legs;
    char name[50];
    
    Animal() { // 기본값 넣어보기 "기본 생성자" 기본생성자가 먼저 실행됨
        printf("Animal 생성자가 실행됨\n"); // 기본생성자 실행 유무 확인

        legs = 4
        strcpy(name, "동물"); // strcpy를 통해 name에 동물이라고 집어넣기
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


class Person : public Animal { // Animal을 상속을 받음 A <- P 사람은 동물이다
public:
    Person() { // Person의 생성자 만들기, Person이라고 하는 객체는 반드시 상위 생성자 Animal 먼저 실행

        printf("Person 생성자가 실행됨\n");
        legs = 2;
        strcpy(name, "사람");
        // 결과 출력시 다리의 개수 : 2, 이름 : 사람이 나옴

    }

    char regist_no[30];
    
};


int main() {
    
    Person *p = new Person(); // new라는 키워드에 의해서 동적 instance가 만들어지게됨
    // new Person(); 의 경우 기본생성자 호출, new Person;이면 생성자를 호출하지 않는다는 의미
       사람이라는 객체도 결국은 printAnimalInfo() 함수를 갖게된다.
    
    p->printAnimalInfo(); // 정적클래스를 선언했을 때는 .으로 했는데 동적클래스를 했을 때는 ->를 이용해서 멤버 불러옴
    
};

 

파라미터가 있는 생성자

파라미터가 있는 생성자를 지정해주게 되면 자식클래스에서는 부모클래스의 생성자를 호출해줘야 한다.

부모클래스의 생성자를 실행하려면 파라미터가 있어야 하기 때문

 

부모클래스가 파라미터를 받는 생성자가 있다면, 자식클래스는 부모클래스의 생성자를 꼭 명시적으로 호출해줘야함

 

class ChildClass : public ParentClass{

    ChildClass( ... ) : ParentClass ( ... ) {

             ...

    }

}

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>


// 부모클래스
class Animal {
public:
    int legs;
    char name[50];
    
    Animal(int legs, const char* name) { // 이 Animal이라는 것은 그 자체로 존재하지 않고 파라미터를 받아서 할당을 해야만 존재가능
        printf("Animal 생성자가 실행됨\n");

        this->legs = legs;
        // this->legs를 legs로 초기화, this가 가리키는 것은 현재의 instance 즉 위에 선언한 legs
        // this-> legs(위에 선언된 int legs의 legs) = legs(파라미터에 선언된 legs)
        strcpy(this->name, name);
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


// 자식클래스
class Person : public Animal
public:
    char regist_no[30];

    // 상속을 주는 클래스에 다른 어떤 파라미터를 받는 생성자가 있다고 했을 때
        상속을 받는 클래스(Person)는 꼭 상속을 주는 클래스 (즉
부모클래스)의 파라미터를 입력을 해줘야함

    Person(const char* regist_no) : Animal(2, "사람") { // 상속을 받는 생성자에를 한 번 더 넣어줘야함
        // 부모클래스의 생성자에서 legs를 받아서 2개로 초기화 해 줌
            대신 이 Person이라는 객체는 주민등록번호를 초기화 해 줘야 되기 때문에 주민등록번호를 입력을 받아야함 const char* regist_no
           따로 입력 받을 거만 () 안에 파라미터로 써주기
        printf("Person 생성자가 실행됨\n");

        strcpy(this->regist_no, regist_no);
    }
    
};

// 자식클래스 하나 더 만들기
class Dog : public Animal {
public:
    Dog() : Animal(4, "개"){
        printf("Dog 생성자가 실행됨\n");
    }
};


int main() {
    
    Person *p = new Person("1234-5463");
    // 이 주민등록번호를 받아서 생성자를 생성해서 Person이 Animal을 초기화시키고
        초기화를 시킨 후에 주민등록번호를 초기화 시킴
    p->printAnimalInfo();
  

    Dog* d = new Dog();
    d-> printAnimalInfo();
};

 

다형성 (Polymorphism)

클래스 인스턴스는 다형성을 통해 하나의 부모 클래스로 관리할 수 있다.

이는 객체 관리에 굉장히 유용한 관점 제공

 

자식클래스는 부모클래스가 될 수 있지만 부모클래스는 자식클래스가 될 수 없다.

Person 클래스와 Dog 클래스는 모두 부모클래스로 Animal을 갖고 있다.

Person 클래스와 Dog 클래스는 모두 다 Animal 클래스가 될 수 있다.

Animal 클래스도 일반적으로 Person 클래스와 Dog 클래스가 될 수 없다.

 

예를 들어 코드상의 Animal* animals[10]이라는 배열을 만들어서 해당 배열 안의 실제 값은

Person, 혹은 Dog 등 여러 가지 객체가 들어갈 수 있기 때문

집합의 개념으로 생각하면 이게 왜 좋은지 알 수 있다.

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>


// 부모클래스
class Animal {
public:
    int legs;
    char name[50];
    
    Animal(int legs, const char* name) {
        printf("Animal 생성자가 실행됨\n");
        this->legs = legs;
        strcpy(this->name, name);
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


// 자식클래스
class Person : public Animal
public:
    char regist_no[30];
  
    Person(const char* regist_no) : Animal(2, "사람") {
        
        printf("Person 생성자가 실행됨\n");
        strcpy(this->regist_no, regist_no);
    }
    
};
// 자식클래스 하나 더 만들기
class Dog : public Animal {
public:
    Dog() : Animal(4, "개"){
        printf("Dog 생성자가 실행됨\n");
    }
};




int main() {
    
    Person *p = new Person("1234-5463");
    p->printAnimalInfo();
  
    Dog* d = new Dog();
    d-> printAnimalInfo();
    
    // Person*과 Dog*클래스는 부모클래스로 Animal을 갖고 있다
    // 그러므로 Person클래스와 Dog클래스는 모두 Animal클래스가 될 수 있다
    // Animal클래스는 Person클래스와 Dog클래스가 될 수 없다
};

 

접근지정자 

; 클래스 멤버의 접근 위치를 강제할 수 있다.

 public, protected, private

public:

이 키워드 안에 있는 멤버는 클래스 내외부에서 자유롭게 접근 가능 (main 함수에서도 접근 가능)

protected:

이 키워드 안에 있는 멤버는 상속된 클래스에서만 자유롭게 접근 가능 (외부에서는 접근 불가능, 내부에서만 접근 가능)

private:

이 키워드 안에 있는 멤버는 클래스 자신에게서만 자유롭게 접근 가능

 

public: 경우

#define _CRT_SECURE_NO_WARNINGS 
#include <cstdio>
#include <cstring>


// 부모클래스
class Animal {
// public, protected, private
public:
    int legs;
    char name[50];
    
    Animal(int legs, const char* name) {
        printf("Animal 생성자가 실행됨\n");
        this->legs = legs;
        strcpy(this->name, name);
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


// 자식클래스
class Person : public Animal {
public:
    char regist_no[30];
    Person(const char* regist_no) : Animal(2, "사람") {
        printf("Person 생성자가 실행됨\n");
        strcpy(this->regist_no, regist_no);
    }
};


class Dog : public Animal {
public:
    Dog() : Animal(4, "개"){
        printf("Dog 생성자가 실행됨\n");
    }
};


int main() {
    
    Person *person = new Person("1234-5463");
    printf("person의 다리수 : %d", person->legs);// Animal클래스는 public 이라 자유롭게 ->로 외부에서 접근 가능
   
};

 

protected: 경우

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>


// 부모클래스
class Animal {
// public, protected, private
protected:
    int legs;
    char name[50];
    
    Animal(int legs, const char* name) {
        printf("Animal 생성자가 실행됨\n");
        this->legs = legs;
        strcpy(this->name, name);
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


// 자식클래스
class Person : public Animal {
public:
    char regist_no[30];
    Person(const char* regist_no) : Animal(2, "사람") {
        printf("Person 생성자가 실행됨\n");
        strcpy(this->regist_no, regist_no);
    }
    
    void printLegs(){ // 자식클래스에서 내부에서 접근 가능
        printf("Person의 다리 수 : %d\n", this->legs);
    }
};


class Dog : public Animal {
public:
    Dog() : Animal(4, "개"){
        printf("Dog 생성자가 실행됨\n");
    }
};


int main() {
    
    Person *person = new Person("1234-5463");
    
   
};

private: 경우

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>


// 부모클래스
class Animal {
// public, protected, private
private:
// private:으로 접근지정자가 걸려있으면 이 클래스 내부에서만 사용 가능, int legs; char name[50];은 바깥에서 절대 실행 불가능

    int legs;
    char name[50];
    
public: // 밑 부분들은 어디에서나 실행 가능하게 public: 사용
    Animal(int legs, const char* name) {
        printf("Animal 생성자가 실행됨\n");
        this->legs = legs;
        strcpy(this->name, name);
        
    }
    
    void printAnimalInfo(){
        printf("다리의 개수 : %d\n", legs);
        printf("이름 : %s\n", name);
    }
};


// 자식클래스
class Person : public Animal {
public:
    char regist_no[30];
    Person(const char* regist_no) : Animal(2, "사람") {
        printf("Person 생성자가 실행됨\n");
        strcpy(this->regist_no, regist_no);
    }
    
    void printLegs(){
        printf("Person의 다리 수 : %d\n", this->legs);
        // private는 상속을 받은 이 클래스에서도 접근 불가, this->legs 안됨 빨간줄 그임
    }
};


class Dog : public Animal {
public:
    Dog() : Animal(4, "개"){
        printf("Dog 생성자가 실행됨\n");
    }
};


int main() {
    
    Person *person = new Person("1234-5463");
    
   
};

 

protected: 와 private: 의 차이는

private는 상속을 받은 클래스에서도 접근 불가능

protected는 상속 받은 클래스는 접근 가능, 하지만 int main(){}; 구문 즉 바깥에서는 접근 불가능

 

상속에서의 접근 지정자

(그냥 pubic:으로 가져온다고 생각하면 됨

class Person : public Animal)

public: 

부모 클래스의 모든 멤버를 그대로 가져옴

protected: 

부모 클래스의 모든 public 멤버를 protected 멤버를 가져와서 protected로 만듦

private:

부모 클래스의 모든 public, protected 멤버를 가져와서 private로 만듦

 

연습 프로그래밍

#define _CRT_SECURE_NO_WARNINGS // strcpy를 사용시 헤더파일
#include <cstdio> // printf나 scanf 사용시 헤더파일
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>


// 판타지 유닛이라는 클래스 생성

// 모든 캐릭터는 자신의 이름이라는 속성이 존재
// printInfo라는 멤버 함수가 존재하며 printInfo라는 멤버함수를 이용해 생성결과 출력


class FantasyUnit {
public:
    int maxHp;
    int atk;
    int range;
    int movementSpeed;
    
    /*
    FantasyUnit(int maxHp, int atk, int range, int movementSpeed) {
        this-> maxHp = maxHp;
        this-> atk = atk;
        this-> range = range;
        this-> movementSpeed = movementSpeed;
    }
     
     이렇게 생성자 만드는 경우 밑에 각각의 클래스들을
     public:
        Slime() : FantasyUnit(100, 4, 5, 4) {
            strcpy(name, "슬라임"); 이런식으로 해줄 수 있다.
        }
    */
    
    char name[50];
    
    void printInfo(){
        printf("%s의 능력치\n", name);
        printf("최대체력: %d\n", maxHp);
        printf("공격력 : %d\n", atk);
        printf("사정거리 : %d\n", range);
        printf("이동속도 : %d\n", movementSpeed);
    }
};


// 상속을 통하여 클래스 설계 완료
class Goblin : public FantasyUnit {
public:
    // 생성자가 내용들을 초기화
    Goblin() {
        maxHp = 60;
        atk = 5;
        range = 10;
        movementSpeed = 10;
        
        strcpy(name, "고블린");
    }
};


class Orc : public FantasyUnit {
public:
    // 생성자가 내용들을 초기화
    Orc() {
        maxHp = 80;
        atk = 10;
        range = 12;
        movementSpeed = 8;
        
        strcpy(name, "오크");
    }
};


class Slime : public FantasyUnit {
public:
    // 생성자가 내용들을 초기화
    Slime() {
        maxHp = 100;
        atk = 4;
        range = 5;
        movementSpeed = 4;
        
        strcpy(name, "슬라임");
    }
};


class SkeletonArcher : public FantasyUnit {
public:
    // 생성자가 내용들을 초기화
    SkeletonArcher() {
        maxHp = 60;
        atk = 20;
        range = 50;
        movementSpeed = 3;
        
        strcpy(name, "해골궁수");
    }
};
#include "lala.hpp"


int main(){
    FantasyUnit* units[10];
    
    for(int i = 0; i < 10; i++) {
        switch (rand() % 4) {
            case 0:
                units[i] = new Goblin();
                break;
            case 1:
                units[i] = new Orc();
                break;
            case 2:
                units[i] = new Slime();
                break;
            case 3:
                units[i] = new SkeletonArcher();
                break;
        }
    }
    
    for (int i = 0; i < 10; i++) {
        units[i]->printInfo();
    }
    
    return 0;
}

 

과제형 연습 프로그래밍

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>




class Item {
public:
    int atk;
    int range;
    int price;
    char name[50];
   
    Item(int atk, int range, int price, const char* name) {
        this-> atk = atk;
        this-> range = range;
        this-> price = price;
        strcpy(this->name, name);
    }
     
    
    void printInfo(){
        printf("아이템 이름 : %s\n", name);
        printf("공격력 : %d\n", atk);
        printf("사정거리 : %d\n", range);
        printf("가격 : %d\n", price);
    }
};


class ShortSword : public Item {
public:
    ShortSword() : Item(10, 5, 500, "숏소드") {}
};
 
class LongSword : public Item {
public:
    LongSword() : Item(20, 10, 2000, "롱소드") {}
};


class Hammer : public Item {
public:
    Hammer() : Item(30, 7, 3000, "해머") {}
};

#include "lala.hpp"


int main(){


    Item* items[10];
    
    srand(time(0)); // 매번 다른 랜덤값
    
    for(int i = 0; i < 10; i++) {
        switch (rand() % 3) {
            case 0:
                items[i] = new ShortSword();
                break;
            case 1:
                items[i] = new LongSword();
                break;
            default: // case 2: 대신 써주면 초기화 안해도 된다.
                items[i] = new Hammer();
                break;
        }
    }
    for (int i = 0; i < 10; i++) {
        items[i]->printInfo();
    }
    
    return 0;
}