My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


파이썬 유저를 위한 C++ (10) - 클래스 (4) - 상속

13) 클래스 (4) - 상속

13-1. 파생 클래스

파이썬에서는 class 자식클래스(부모클래스1, 부모클래스2, ...)의 형식으로 클래스 상속을 할 수 있었습니다.

C++에서도 비슷하게 파생클래스(derived class)라는 개념이 존재하여 부모클래스(base class)의 멤버들을 상속받을 수 있습니다.

예시) 문법

class 파생클래스이름 : 접근제한자1 클래스1, 접근제한자2 클래스2...
{
    // 파생클래스 멤버 리스트
}

상속을 받을 때, 옵션으로 부모 클래스에 대한 접근제한자를 설정해야 합니다.

13-2. 클래스 접근제한자

이전에는 클래스 안의 멤버에 접근제한자를 설정하여 접근 권한을 조정했습니다.

상속을 위해서는 부모 클래스에도 접근제한자를 설정해야하는데요,

역시 접근제한자는 public, private, protected 3가지가 있으며, 이에 따른 차이는 다음과 같습니다.

  • Type of Inheritance
    1. Public Inheritance
      • 부모 클래스의 public 멤버: 파생 클래스의 public 멤버와 동일하게 취급
      • 부모 클래스의 protected 멤버 : 파생 클래스의 protected 멤버와 동일하게 취급
    2. Protected Inheritance
      • 부모 클래스의 public, protected 멤버 : 파생클래스의 protected 멤버와 동일하게 취급
    3. Private
      • 부모 클래스이 public, protected 멤버 : 파생클래스의 private 멤버와 동일하게 취급
  • 공통점 : 부모 클래스의 private 멤버는 직접 접근이 불가능함. 오직 public & protected 멤버를 이용하여 접근가능함.

13-3 protected 접근제한자

protected 접근제한자는 파생 클래스와 관련이 있습니다.

Access public protected private
Same class yes yes yes
Derived classes yes yes no
Outside classes yes no no

부모 클래스에서 protected로 설정된 멤버는 파생 클래스에서는 접근이 가능합니다.

ex) Shape 클래스의 파생 클래스에서 부모 멤버에 접근하기

class Shape{
protected:
    std::string name_;
};

class Rectangular: public Shape{
public:
	void SetName(std::string name);
	void PrintName();
};

void Rectangular::SetName(std::string name){
	name_ = name; // 만약 name_이 private면 이게 안됨!
}

void Rectangular::PrintName(){
	std::cout << "모양의 이름: " << name_;
}

int main(){
    Rectangular rec;
    // rec.name_ 과 같은 접근은 허용되지 않음!
	rec.SetName("직사각형");
	rec.PrintName(); // 모양의 이름: 직사각형
}

그 외에도 protected 멤버는 프렌드 클래스에서도 접근이 가능하다는 특징이 있습니다.

13-4. 생성자

이제 파생 클래스의 생성자에 대해서 알아보겠습니다.

ex) 파생클래스의 생성자를 정의하는 방식

Rectangular::Rectangular(std::string name, int height, int width): Shape(name){ // 파생클래스(매개변수): 부모클래스(인수)
    height_ = height, width_ = width;
}

부모클래스의 생성자를 상속하는 느낌으로 파생클래스의 생성자를 정의할 수 있습니다.

이 때, 부모클래스의 생성자가 먼저 호출되기 때문에 name 변수는 이미 초기화되었으므로 굳이 함수에 넣지 않아도 될 것입니다.

ex) 파생클래스의 생성자를 호출하기

class Shape{
protected:
    std::string name_;
public:
    Shape(std::string name);
};

class Rectangular: public Shape{
private:
    int width_, height_;
public:
    Rectangular(std::string name, int width, int height);
	void PrintName();
	void PrintLength();
};

Shape::Shape(std::string name){
    name_ = name;
}

Rectangular::Rectangular(std::string name, int width, int height): Shape(name){
    width_ = width, height_ = height;
}

void Rectangular::PrintName(){
	std::cout << "모양의 이름: " << name_ << std::endl;
}

void Rectangular::PrintLength(){
	std::cout << "가로 : " << width_ << " 높이 : " << height_;
}

int main(){
    Rectangular rec = {"직사각형", 10, 15};
	rec.PrintName(); // 모양의 이름: 직사각형
	rec.PrintLength(); // 가로 : 10 높이 : 15
}

13-5. 예외 : 상속받지 않는 메서드들

파생 클래스는 부모 클래스의 대부분을 상속받지만, 예외도 있습니다.

  • 부모 클래스의 생성자
  • 부모 클래스의 연산자 오버로딩
  • 부모 클래스의 프렌드 함수

13-6. 함수 오버라이딩

함수 오버라이딩(Overriding)은 부모 클래스에서 정의된 함수를 자식 클래스에서 덮어씌울 수 있는 기능을 말합니다.

ex) Shape 함수의 PrintInfo 함수를 덮어씌우기

void Shape::PrintInfo(){
	std::cout << "모양의 이름: " << name_ << std::endl;
}

void Rectangular::PrintInfo(){
	std::cout << "직사각형의 가로 : " << width_ << " 높이 : " << height_<< std::endl;
}

이런 식으로 네임스페이스를 파생클래스로 좁혀서 선언함으로써 파생클래스의 메서드를 재정의할 수 있습니다.

ex) 사용 예시

class Shape{
protected:
    std::string name_;
public:
    Shape(std::string name);
    void PrintInfo();
};

class Rectangular: public Shape{
private:
    int width_, height_;
public:
    Rectangular(std::string name, int width, int height);
    void PrintInfo();
};

int main(){
    Rectangular rec = {"정사각형", 10, 15};
    rec.PrintInfo(); // 직사각형의 가로 : 10 높이 : 15
    rec.Shape::PrintInfo(); // 모양의 이름: 정사각형
}

물론, 부모 클래스의 메서드는 여전히 남아있기 때문에,

네임스페이스를 적절히 조절하여 부모 클래스의 메서드도 호출할 수 있습니다.

13-7. 파이썬 상속과 C++ 상속의 차이

파이썬에서의 상속

  • super함수를 통해서 부모 클래스의 인스턴스를 직접 다룰 수 있음.
  • 여러번 상속되었거나, 다중상속의 경우 MRO(Method Resolution Order)에 의해서 부모 클래스의 인스턴스를 순서대로 호출할 수 있음.

C++에서의 상속

  • 부모 클래스 인스턴스 자체를 다룰 수는 없음.

    단지, 부모 클래스의 멤버들이 파생 클래스에 편입되는 방식일 뿐.

  • 그렇기 때문에 연쇄적으로 부모 클래스의 멤버를 호출하는게 아니라, 이미 파생클래스의 멤버변수로 존재하므로 그냥 호출하면 됨.

comments powered by Disqus