SA성아 2023. 11. 22. 12:30

상속 (inheritance)


#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";}
};
int main() {
	A aa;
	return 0;
}

aa.을 찍었더니 4개가 뜸
A라는 부모로부터 상속받음

#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 << "c2\n"; }
};
class B : public A { }; //파생클래스, 자식클래스
int main() {
	A aa;
	aa.b1();
	B bb;
	//bb.
}

 

C++에서 상속하는 방법

// 기본 클래스 Animal 정의
class Animal {
public:
    void eat() {
        cout << "나는 먹는다.\n";
    }
};

// 파생 클래스 Dog 정의, Animal 클래스를 상속
class Dog : public Animal {
public:
    void bark() {
        cout << "멍멍!\n";
    }
};

int main() {
    Dog myDog; // Dog 객체 생성
    myDog.eat(); // 기본 클래스의 메소드 호출
    myDog.bark(); // 파생 클래스의 메소드 호출
    return 0;
}

 

프로그래밍 언어의 상속

1. Java

// 기본 클래스 Animal 정의
class Animal {
    void eat() {
        System.out.println("나는 먹는다");
    }
}

// 파생 클래스 Dog 정의, Animal 클래스를 상속
class Dog extends Animal {
    void bark() {
        System.out.println("멍멍!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();  // Dog 객체 생성
        myDog.eat();  // 기본 클래스의 메소드 호출
        myDog.bark();  // 파생 클래스의 메소드 호출
    }
}

2. Python

# 기본 클래스 Animal 정의
class Animal:
    def eat(self):
        print("나는 먹는다")

# 파생 클래스 Dog 정의, Animal 클래스를 상속
class Dog(Animal):
    def bark(self):
        print("멍멍!")

myDog = Dog()  # Dog 객체 생성
myDog.eat()  # 기본 클래스의 메소드 호출
myDog.bark()  # 파생 클래스의 메소드 호출

3. JavaScript (ES6이상)

// 기본 클래스 Animal 정의
class Animal {
    eat() {
        console.log("나는 먹는다");
    }
}

// 파생 클래스 Dog 정의, Animal 클래스를 상속
class Dog extends Animal {
    bark() {
        console.log("멍멍!");
    }
}

let myDog = new Dog();  // Dog 객체 생성
myDog.eat();  // 기본 클래스의 메소드 호출
myDog.bark();  // 파생 클래스의 메소드 호출

 

클래스들의 계층구조

10p

 

상속 과정

11p

 

기본클래스와 파생클래스 : is-a, is_a, is a 관계

12p

 

클래스 상속 형식

13p

 

상속 접근제어 속성에 따른 파생 클래스 멤버의 속성변화

13p(시험에자주나옴)

 

public 상속 접근제어

15p

 

class Dog : public Animal

16p

▶ 상속을 받게되면 부모의 private를 제외하고 protected와 public이 그대로 넘어온다.

 

상속

#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 main() {
	A aa;
	aa.setX(1);
	aa.showX(); 
	B bb;
	bb.setX(10);
	bb.showX();
	return 0;
}

▶ bb가 setX와 showX를 사용할 수 있는 이유는 부모로부터 상속받았기 때문

 

실습 8-1: 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.setX(1); // 오류 ① bb.setX(1);
	bb.setY(2); // 오류 ② bb.setY(2);
	bb.showX(); // 기본클래스의 멤버접근
	bb.showY(); // 파생클래스의 멤버접근
	return 0;
}

▶ setX와 showX를 상속받음, x는 private이기 때문에 상속받을 수 없음

 

실습 8-2: 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() { 
		showX();  
		cout << y << endl; 
	}
};
int main()
{
	B bb;
	bb.setX(1); // 기본클래스의 멤버접근
	bb.setY(2); // 파생클래스의 멤버접근
	bb.showX(); // 기본클래스의 멤버접근
	bb.showXY(); // 파생클래스의 멤버접근
	return 0;
}

 

private 상속 접근제어

20p

 

class Dog : private Animal

21p

 

실습 8-3: 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); // 오류
	bb.setY(2); // 파생클래스의 멤버접근

	//bb.showX(); // 오류
	bb.showY(); // 파생클래스의 멤버접근
	return 0;
}

▶ 상속이 되긴 하는데 자식클래스 안에서만 사용할 수 있다.

class B :private A {
	int y;
	void setX(int i) { x = i; }
	void showX() { cout << x << endl; }
public:
	void setY(int i) { y = i; }
	void showY() { cout << y << endl; }
};

 

In-class member initializers

#include <iostream>
using std::cout;
using std::endl;
class A
{
	int x = 1;// In-class member initializers: 클래스 안에서 멤버값을 바로 초기화하는 방법
public:
	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; } // 아래와 똑같은 소스
	//A() : x(2) {}

	void setX(int i) { x = i; }
	int getX() { return x; }
};
int main()
{
	A a1; //디폴트 생성자는 사라짐
	cout << a1.getX() << endl;
	return 0;
}
//2

 

실습 8-4: 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; }
	// 기본 클래스의 public 멤버 접근
	void showXY() { showX(); cout << y << endl; }
};
int main()
{
	B bb;
	bb.setXY(1, 2); // 파생클래스의 멤버접근
	bb.showXY(); // 파생클래스의 멤버접근
	return 0;
}

▶ private으로 상속받더라도, 기본 클래스의 public 멤버들은 파생 클래스의 멤버함수에 의해서는 접근될 수 있다.

▶ private 상속은 기본클래스의 public 멤버를 파생클래스에서만 접근 가능하게 만든다. 즉, 자식에게만 비밀스럽게 상속한다.

 

protected 상속 접근제어 속성

25p

class Dog : protected  Animal

26p

실습 8-5: protected 멤버변수

#include <iostream>
using std::cout;
using std::endl;
class A
{
protected: //private이라면?
	int a, b;
public:
	void setAB(int i, int j) { a = i; b = j; }
};
class B :public A
{
	int c; // private
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;
}

▶ 기본 클래스 A에서 a, b는 protected이므로 파생 클래스 B에서 접근이 가능하다. 

▶ private이면 불가능

 

protected멤버+public상속(많이 사용하는 스타일)

class A
{
protected: //private이라면?
	int a, b;
public:
	void setAB(int i, int j) { a = i; b = j; }
};
class B :public A
{
	int c; //private
public:
	void setC(int n) { c = n; }
	void showABC() { cout << a << b << c << endl; }
};

▶ 클래스를 만들 때 클래스 외부에서는 접근하지 못하게 하고 자식클래스에서는 마음대로 접근할 수 있게 하려면 멤버의 속성을 protected로 사용해야 한다.

▶ 상속은 public으로 하자

 

실습 8-6: 상속에서 생성자와 소멸자

#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;
}
//A의 생성자
//B의 생성자
//B의 소멸자
//A의 소멸자

▶ 부모와 자식에게 생성자와 소멸자가 있을 경우에는, 생성자는 부모가 먼저 소멸자는 자식이 먼저 호출된다.

 

실습 8-7: 파생 클래스 생성자에서 기본 클래스 생성자에 매개변수전달 형식

#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는 기본클래스 생성자의 매개변수로 전달
		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;
}

 

실습 8-8: 계층적 다중상속에서계층적 매개변수전달 1

#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) {
		// 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;
}

 

실습 8-9: 계층적 다중상속에서 계층적 매개변수 전달 2

#include <iostream>
using std::cout;
using std::endl;
class B { //할아버지
	double d;
public:
	B(double dd) { d = dd; }
	double getD() { return d; }
};
class D1 :public B { //아버지
	int i;
public:
	D1(double dd, int ii) :B(dd) { i = ii; }
	int getI() { return i; }
};
class D2 :public D1 { //자식
	char c;
public:
	D2(double dd, int ii, char cc) :D1(dd, ii) { c = cc; }
	void print() {
		cout << "Double : " << getD() << endl;
		// B 멤버 호출
		cout << "Int : " << getI() << endl;
		// D1 멤버 호출
		cout << "Char : " << c << endl;
	}
};
int main()
{
	D2 d2(10.5, 10, 'H');
	cout << d2.getD() << ',' << d2.getI() << endl;
	// B, D1 멤버 호출
	d2.print();
	return 0;
}

출처: C++ 프로그래밍