프로그램/C,C++
[C++ 기본3] 5. 상속
naudhizb
2022. 5. 15. 15:43
반응형
상속은 객체지향언어에서 가장 중요한 개념중에 하나이다.
상속을 통하여 코드 재활용성의 가능성을 높일 수 있다.
C++에서는 상속을 위하여 클래스 뒤에 상속받는 클래스의 이름을 콜론(:)으로 분리하여 명시한다.
// 5_상속1.cpp 132page ~
#include <iostream>
#include <string>
class People
{
std::string name;
int age;
};
class Student : public People // 상속 ( inheritance )
{
int id;
};
class Professor : public People
{
int major;
};
int main()
{
}
private 지시어로 붙은 멤버 변수의 경우 파생 클래스에서도 접근이 불가능하다.
// 5_상속2
class Base // base class, super class , 기반 클래스
{
private: // 자신의 멤버에서만 접근 가능
int a;
protected: // 자신과 파생클래스에서 접근가능
int b;
public: // 어디서나 접근 가능.
int c;
};
class Derived : public Base // Derived class, subclass, 파생클래스
{
public:
void foo() {
a = 0; // error. 기반 클래스의 private 멤버는
// 파생클래스라도 접근할수 없다.
c = 0; // ok
}
};
int main()
{
Base base;
base.a = 0; // error
base.c = 0; // ok
}
파생클래스를 사용하기 위해서는 기반클래스의 생성자 또한 호출되어야 한다. 하지만, 이 경우 컴파일러가 기반 클래스의 default 생성자를 호출하기 때문에 사용자가 다른 동작을 원하는 경우, 기반클래스의 디폴트 생성자가 정의되어 있지 않은 경우 사용자가 직접 기반클래스의 생성자를 호출해주어야 한다.
// 상속과 생성자. 135page ~
#include <iostream>
// 핵심
// 1. 파생 클래스의 초기화 리스트 목록에서 기반 클래스의 생성자를
// 호출하는 코드를 컴파일러가 생성해 주는것
// 2. 기반 클래스의 생성자는 기본적으로 default 생성자 호출
// 변경하려면 사용자가 초기화 리스트에서 기반 클래스생성자를
// 명시적으로 호출해야 한다.
// 3. 기반 클래스에 디폴트 생성자가 없으면 반드시 파생 클래스에서
// 명시적으로 호출해야 한다.
class Base
{
public:
// Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
~Base() { std::cout << "~Base()" << std::endl; }
};
class Derived : public Base
{
public:
// Derived() { std::cout << "Derived()" << std::endl; } // error
Derived() : Base(0) { std::cout << "Derived()" << std::endl; } // error
Derived(int a) : Base(a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
};
int main()
{
//Derived d;
Derived d(1);
}
생성자를 protected로 설정하게 되면 해당 클래스를 추상 클래스로 만드는 효과가 있다. C++에서는 인터페이스를 만들 때에도 해당 방식을 사용할 수 있다.
// 139 page
// protected 생성자 : 자신의 객체는 생성할수 없지만(추상적 존재, abstract)
// 파생 클래스의 객체는 생성할수 있게(구체적 존재, concrete) 하는 기술
class Animal
{
protected:
Animal() {}
};
class Dog : public Animal
{
};
int main()
{
// 다음중 에러를 모두 고르세요
Animal a; // 1. error
Dog d; // 2. ok
}
파생클래스에서 기반클래스의 멤버로의 접근을 막기 위해서는 접근 변경자를 이용해서 권한을 변경할 수 있다. 단, 접근 권한의 확대는 불가능하다.
// 접근 변경자
class Base
{
private: int a;
protected: int b;
public: int c;
};
class Derived : private Base // 접근 변경자
{ // 기반 클래스의 접근 권한을
}; // 축소할때 사용. 확대는 안됨
int main()
{
Base base; base.c = 10; // ok
Derived derv; derv.c = 10; // error
}
코드 wrapping을 수행할 때 유저에게 허용되지 않은 동작을 감추고자 할 때 유용하게 사용할 수 있습니다.
// 이미 list가 있습니다.
#include <list>
// 그런데, 사용자가 스택을 요구 합니다.
template<typename T> class stack : private std::list<T>
{
public:
void push(const T& a) { std::list<T>::push_back(a); }
void pop() { std::list<T>::pop_back(); }
T& top() { return std::list<T>::back(); }
};
int main()
{
stack<int> s;
s.push(10);
//s.push_front(30); // error. private 입니다.
}
반응형