상속은 C++에서 객체 지향 프로그래밍의 핵심 기능 중 하나입니다. 기존 클래스(기반 클래스)로부터 새로운 클래스(파생 클래스)를 생성할 수 있게 해줍니다.
파생 클래스는 기반 클래스의 기능을 상속받으며 자체적인 추가 기능을 가질 수 있습니다. 예를 들어:
class Animal {
// eat() 함수
// sleep() 함수
};
class Dog : public Animal {
// bark() 함수
};
여기서 Dog 클래스는 Animal 클래스로부터 파생되었습니다. Dog가 Animal로부터 파생되었으므로 Animal의 멤버에 Dog에서 접근할 수 있습니다.
public 키워드를 사용해 Dog를 Animal로부터 상속받는 것을 주목하세요.
class Dog : public Animal {...};
private와 protected 키워드도 public 대신 사용할 수 있습니다. 이에 대한 차이점은 이 튜토리얼 뒷부분에서 학습하겠습니다.
is-a 관계
상속은 is-a 관계입니다. 두 클래스 사이에 is-a 관계가 있을 때만 상속을 사용합니다.
예시:
자동차는 탈것이다.
오렌지는 과일이다.
외과의사는 의사이다.
개는 동물이다.
예제 1: C++ 상속 간단 예제
// C++ 상속을 보여주는 프로그램
#include <iostream>
using namespace std;
// 기반 클래스
class Animal {
public:
void eat() {
cout << "I can eat!" << endl;
}
void sleep() {
cout << "I can sleep!" << endl;
}
};
// 파생 클래스
class Dog : public Animal {
public:
void bark() {
cout << "I can bark! Woof woof!!" << endl;
}
};
int main() {
// Dog 클래스 객체 생성
Dog dog1;
// 기반 클래스 멤버 호출
dog1.eat();
dog1.sleep();
// 파생 클래스 멤버 호출
dog1.bark();
return 0;
}
출력
I can eat!
I can sleep!
I can bark! Woof woof!!
여기서 dog1(파생 클래스 Dog의 객체)은 기반 클래스 Animal의 멤버에 접근할 수 있습니다. Dog가 Animal로부터 상속받았기 때문입니다.
// Animal 클래스 멤버 호출
dog1.eat();
dog1.sleep();
C++ protected 멤버
접근 지정자 protected는 C++ 상속에서 특히 중요합니다.
private 멤버처럼 protected 멤버도 클래스 외부에서는 접근할 수 없습니다. 그러나 파생 클래스와 프렌드 클래스/함수에서는 접근할 수 있습니다.
클래스의 데이터를 숨기면서도 파생 클래스에서 상속받을 수 있게 하려면 protected 멤버가 필요합니다.
protected에 대해 더 알아보려면 C++ 접근 지정자 튜토리얼을 참조하세요.
예제 2: C++ protected 멤버
// C++ protected 멤버를 보여주는 프로그램
#include <iostream>
#include <string>
using namespace std;
// 기반 클래스
class Animal {
private:
string color;
protected:
string type;
public:
void eat() {
cout << "I can eat!" << endl;
}
void sleep() {
cout << "I can sleep!" << endl;
}
void setColor(string clr) {
color = clr;
}
string getColor() {
return color;
}
};
// 파생 클래스
class Dog : public Animal {
public:
void setType(string tp) {
type = tp;
}
void displayInfo(string c) {
cout << "I am a " << type << endl;
cout << "My color is " << c << endl;
}
void bark() {
cout << "I can bark! Woof woof!!" << endl;
}
};
int main() {
// Dog 클래스 객체 생성
Dog dog1;
// 기반 클래스 멤버 호출
dog1.eat();
dog1.sleep();
dog1.setColor("black");
// 파생 클래스 멤버 호출
dog1.bark();
dog1.setType("mammal");
// dog1의 getColor()를 인수로 사용
// getColor()는 string 데이터 반환
dog1.displayInfo(dog1.getColor());
return 0;
}
출력
I can eat!
I can sleep!
I can bark! Woof woof!!
I am a mammal
My color is black
여기서 변수 type은 protected이므로 파생 클래스 Dog에서 접근 가능합니다. setType() 함수로 Dog 클래스에서 type을 초기화한 것을 볼 수 있습니다.
반면 private 변수 color는 Dog에서 초기화할 수 없습니다.
class Dog : public Animal {
public:
void setColor(string clr) {
// 오류: "Animal::color" 멤버에 접근 불가
color = clr;
}
};
또한 protected 키워드가 데이터를 숨기므로 Dog나 Animal 클래스 객체에서 직접 type에 접근할 수 없습니다.
// 오류: "Animal::type" 멤버에 접근 불가
dog1.type = "mammal";
C++ 상속의 접근 모드
이전 튜토리얼에서 public, private, protected 같은 C++ 접근 지정자에 대해 배웠습니다.
지금까지 기존 기반 클래스로부터 클래스를 파생할 때 public 키워드를 사용했습니다. 그러나 private과 protected 키워드로도 클래스를 상속받을 수 있습니다. 예를 들어:
class Animal {
// 코드
};
class Dog : private Animal {
// 코드
};
class Cat : protected Animal {
// 코드
};
클래스를 파생하는 이러한 다양한 방식을 접근 모드라고 합니다. 이 접근 모드들은 다음과 같은 효과를 가집니다:
public: 파생 클래스가 public 모드로 선언되면 기반 클래스의 멤버가 그대로 파생 클래스에 상속됩니다.
private: 이 경우 기반 클래스의 모든 멤버가 파생 클래스에서 private 멤버가 됩니다.
protected: 기반 클래스의 public 멤버가 파생 클래스에서 protected 멤버가 됩니다.
기반 클래스의 private 멤버는 파생 클래스에서도 항상 private입니다.
자세한 내용은 C++ public, private, protected 상속 튜토리얼을 참조하세요.
상속에서의 멤버 함수 오버라이딩
기반 클래스와 파생 클래스에 같은 이름과 인수를 가진 멤버 함수가 있다고 가정해 봅시다.
파생 클래스 객체를 생성해 그 멤버 함수에 접근하려 하면 기반 클래스가 아닌 파생 클래스의 멤버 함수가 호출됩니다.