C++

10-1 상속

리버윤 2023. 11. 22. 12:33
728x90

 

 상속성(Inheritance) :

C에서는 새로운 내용을 추가할 수 없다, (printf()함수를 수정하고싶다 -> X) 

하지만 객체지향언어인 C++에서는 상속성을 제공하여 이미 존재하는 클래스로부터 기본적인 특성을 물려받아 그대로 사용하고 새로운 특성만 추가로 정의할 수 있다,

 

 - 장점 : 기존의 클래스를 그대로 사용할 수 있는 장점이 있고 새로운 클래스를 보다 쉽게 만들 수 있다.

 

class B : public A             // class B : public 부모

public만 물려받는다.

 

상속 : 코드 재사용

#include <iostream>
using std::cout;

class A // 기본 클래스, 부모 클래스 
{
private:
	void a1() {cout << "a1\n"; }
	void a2() {cout << "a2\n"; }
public:
	void b1() {cout << "b1\n"; }
	void b2() {cout << "b2\n"; }
	void b3() {cout << "b3\n"; }
	void b4() {cout << "b4\n"; }
protected: //자식에게만
	void c1() {cout << "c1\n"; }
	void c2() {cout << "c1\n"; }
};

class B { };
//class B : public A {  //파생클래스, 자식클래스
};

int main()
{
	A aa;
	aa.b1();
	B bb;
	bb.
}

-> A 클래스로부터 public으로 상속받은 B 클래스

 

여러 프로그램 언어의 상속 예제

1. python

class Animal:  # 부모 클래스
    def speak(self):
        pass

class Dog(Animal):  # 자식 클래스
    def speak(self):
        return "멍멍"

class Cat(Animal): # 자식 클래스
    def speak(self):
        return "야옹"

 

2. JAVA

class Animal {  // 부모 클래스
    void speak() {
    }
}

class Dog extends Animal {  // 자식 클래스
    void speak() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {  // 자식 클래스
    void speak() {
        System.out.println("야옹");
    }
}

 

3. kotlin

 

 

클래스 다이어그램을 그릴 때는 파생에서 기본으로 화살표가 향하게. 빈 화살표로 그림.

 

부모 (superclass),   기본(Base) 클래스

자식 (subclass),   파생(Derived) 클래스

 

- is -a 관계

 

  • class 파생클래스명 : 상속접근제어 기본클래스명 {  };
  • class Dog : public Animal {  };
    • Animal 클래스로부터 public으로 상속받은 Dog클래스

 

부모의 private은 상속불가.

protected은 protected로,  public은 public으로 바뀌어서 가져간다.

- public 상속 접근인 경우에는 기본 클래스의 모든 public, protected멤버들은 파생 클래스의 public, protected 멤버가 된다.

 

 

  • Animal이라는 부모가 있다고할 때 Dog가 상속을 받는 경우 

 

 

  • 상속 예제
#include <iostream>
using std::cout;
using std::endl;

class A // 기본 클래스 
{
		int x;  //아무것도 없으면 private
	public:
		void setX(int i) {x =i;}
		void showX() { cout << x << endl; }
};
class B : public A //파생 클래스
{
	//아무 것도 없어요. 그러나!
};
int main() {
	A aa;
	aa.setX(1);
	aa.showX(); 
	B bb;
	bb.setX(10);
	bb.showX();
	return 0;
}

int x를 제외한 public을 상속받음

 

  • public 상속 접근제어 예제 1
#include <iostream>
using std::cout;
using std::endl;

class A // 기본 클래스
{
	int x;
public:
	void setX(int i) { x = i; }
	void showX() { cout << x << endl; }
};
class B :public A //파생 클래스
{
	int y;
public:
	void setY(int i) { y = i; }
	void showY() { cout << y << endl; }
};
int main()
{
	B bb; // 파생클래스의 객체
	bb.x = 1; // 오류-부모의 private이기 때문에. ① bb.setX(1);
	bb.y = 2; // 오류-y가 private이기 때문에. ② bb.setY(2);
	bb.showX(); // 기본클래스의 멤버접근
	bb.showY(); // 파생클래스의 멤버접근
	return 0;
}

 

  • public 상속 접근제어 예제 2
#include <iostream>
using std::cout;
using std::endl;
class A
{
	int x;
public:
	void setX(int i) { x = i; }
	void showX() { cout << x << endl; }
};
class B :public A
{
	int y;
public:
	void setY(int i) { y = i; }
	void showXY() { cout << x << y << endl; } //오류 - x가 부모의 private이기때문에 상속되지 않아 사용할 수 없음.
  //void showXY() { showX(); cout << y << endl; }   //x를 출력하는 showX를 사용하면 됨.
};
int main()
{
	B bb;
	bb.setX(1); // 기본클래스의 멤버접근
	bb.setY(2); // 파생클래스의 멤버접근
	bb.showX(); // 기본클래스의 멤버접근
	bb.showXY(); // 파생클래스의 멤버접근
	return 0;
}

 

 

  • privat 상속 : 부모의 private는 상속되지 않고 protected와 public은 private로 바뀌어서 상속된다.
    •  자식만 접근 가능 (파생클래스의 멤버함수에 의해서만 접근 가능)

  • private 상속 접근제어 예제 1
#include <iostream>
using std::cout;
using std::endl;
class A
{
	int x; //int x=10; //가능?
public:
	void setX(int i) { x = i; }
	void showX() { cout << x << endl; }
};

class B :private A //비공개적으로 상속
{
	int y;
public:
	void setY(int i) { y = i; }
	void showY() { cout << y << endl; }
};

int main()
{
	A aa;
	B bb;
	aa.setX(1);
	aa.showX();
	bb.setX(1); // 오류 - 상속을 private로 받았기 때문에 setX를 사용 못함.
	bb.setY(2); // 파생클래스의 멤버접근
	bb.showX(); // 오류 - 상속을 private로 받았기 때문에
	bb.showY(); // 파생클래스의 멤버접근
	return 0;
}

 

 

  • In-class member initializers
#include <iostream>
using std::cout;
using std::endl;
class A
{
	int x = 1;// In-class member initializers - 클래스 안에서 멤버값을 초기화 해주는 방법
public:
	//A() {} 디폴트 생성자 눈에 안보임
	void setX(int i) { x = i; }
	int getX() { return x; }
};
int main()
{
	A a1; //디폴트 생성자 호출, 눈에 안보이는 A(){}
	cout << a1.getX() << endl;
	return 0;
}
//1

 

#include <iostream>
using std::cout;
using std::endl;
class A
{
	int x = 1;
public:
	A() { x = 2; }    //디폴트 생성자. 생성자는 예외로 void 안씀
//	A() : x(2) {}  //8번째줄과 같은 소스임.
	void setX(int i) { x = i; }
	int getX() { return x; }
};
int main()
{
	A a1; //디폴트 생성자는 사라짐
	cout << a1.getX() << endl;
	return 0;
}
//2

 

 

  • private 상속 제어접근 용도
#include <iostream>
using std::cout;
using std::endl;

class A
{
	int x;
public:
	void setX(int i) { x = i; }
	void showX() { cout << x << endl; }
};

class B :private A //비공개적으로 상속받는다.
{
	int y;
public:
	void setXY(int i, int j) { setX(i); y = j; } //setx = x.   자식은 접근 가능하니까 setX사용 가능한거임.
	// 기본 클래스의 public 멤버 접근
	void showXY() { showX(); cout << y << endl; }
};

int main()
{
	B bb;
	bb.setXY(1, 2); // 파생클래스의 멤버접근
	bb.showXY(); // 파생클래스의 멤버접근
	return 0;
}

 

 

  • protected 상속 접근제어 : 넘어갈 때 모두 protected로 바뀌어서 넘어간다. 
    • 자식 class에 protected부분이 없다면 private부분으로 상속된다.

 

private과 protected의 공통점은 외부에서 접근이 불가능 하다는 것. 

차이점은 부모가 private는 자식에게 상속 안되는데 protcted는 자식에게 상속 된다는 점.

 

  • protected 멤버 변수
#include <iostream>
using std::cout;
using std::endl;

class A
{
protected: //private이라면? - 자식인 class B에서 int a, b를 접근하지 못해 오류 발생
	int a, b;
public:
	void setAB(int i, int j) { a = i; b = j; }
};

class B :public A
{
	int c; // private
//ptorected
//  int a, b;
public:
	void setC(int n) { c = n; }
	void showABC() { cout << a << b << c << endl; }
	//기본 클래스의 protected 멤버들은
	//파생 클래스의 멤버에 의해 접근될 수 있다.
};
int main()
{
	A aa;
	B bb;
	aa.a; //외부에서는 접근불가
	bb.b; //외부에서는 접근불가
	bb.setAB(1, 2);
	bb.setC(3);
	bb.showABC();
	return 0;
}

 

 

  •  외부에서는 접근을 막고 자식클래스에서는 접근 가능하게 하려면 멤버의 속성을 protected로 사용
  • 상속은 public으로!

 

  • 상속에서 생성자와 소멸자
#include <iostream>
using std::cout;

class A
{
public:
	A() { cout << "A의 생성자\n"; }
	~A() { cout << "A의 소멸자\n"; }
};
class B :public A
{
public:
	B() { cout << "B의 생성자\n"; }
	~B() { cout << "B의 소멸자\n"; }
};
int main()
{
	B ob;
	return 0;
}


//실제 B 클래스의 구성
class B :public A
{
public:
	A() { cout << "A의 생성자 "; }
	~A() { cout << "A의 소멸자 "; }
	B() { cout << "B의 생성자 "; }
	~B() { cout << "B의 소멸자 "; }
};
// 상속을 받으면 부모의 생성자가 먼저 호출됨. 소멸자는 역순으로 실행됨.

 

생성자는 부모가 먼저. 소멸자는 역순.

 

  • 매개변수가 있는 생성자를 갖는 클래스의 상속
#include <iostream>
using std::cout;
using std::endl;

class A {
	int a;
public:
	A(int i) {
		cout << "A의 생성자\n";
		a = i;
	}
	~A() { cout << "A의 소멸자\n"; }
	void showA() { cout << a << '\n'; }
};
class B :public A {
	int b;
public:
	B(int i, int j) :A(i) {// i는 기본클래스 생성자의 매개변수로 전달. 자식쪽에서 부모쪽(int i)으로 전달
		cout << "B의 생성자\n";
		b = j;
	}
	~B() { cout << "B의 소멸자\n"; }
	void showB() { cout << b << endl; }
};

int main()
{
	B bb(10, 20);
	bb.showA();
	bb.showB();
	return 0;
}

 

  • 계층적 다중 상속
#include <iostream>
using std::cout;
using std::endl;

class A //할아버지
{
	int a;
public:
	A(int i) { a = i; }
	int getA() { return a; }
};
class B :public A //아버지
{
	int b;
public:
	B(int i, int j) :A(i) {  //A의 자식이라서 i뿐만아니라 j도 만들어서 A(i)는 class A에 생성자 매개변수로 전달 
		// i는 기본 클래스 A의
		//생성자 매개변수로 전달됨
		b = j;
	}
	int getB() { return b; }
};
class C :public B //자식
{
	int c;
public:
	C(int i, int j, int k) :B(i, j) {
		// i, j는 클래스 B의 생성자 매개변수로 전달됨
		c = k;
	}
	void show() {
		cout << getA() << ' ' << getB() << ' ' << c << endl;
	}
};
int main()
{
	C cc(10, 20, 30);
	cc.show();
	cout << cc.getA() << ' ' << cc.getB() << endl;
	return 0;
}
728x90