인덕대 C++-출처 smile han

12주차

Plki 2024. 11. 18. 16:51

#include <iostream> // 입력 및 출력을 위한 라이브러리 포함
using std::cout; // std 네임스페이스의 cout 사용
// 기본 클래스 A 정의
class A // 기본 클래스, 부모 클래스
{
private:
// private 멤버 함수, 외부에서 접근 불가
void a1() { cout << "a1\n"; } // "a1"을 출력하는 함수
void a2() { cout << "a2\n"; } // "a2"를 출력하는 함수

protected: // protected 멤버는 파생 클래스에서 접근 가능하지만 외부에서는 접근 불가
void p1() { cout << "p1\n"; } // "p1"을 출력하는 함수
void p2() { cout << "p2\n"; } // "p2"를 출력하는 함수

public:
// public 멤버 함수는 외부에서 접근 가능
void b1() { cout << "b1\n"; } // "b1"을 출력하는 함수
void b2() { cout << "b2\n"; } // "b2"를 출력하는 함수
void b3() { cout << "b3\n"; } // "b3"을 출력하는 함수
void b4() { cout << "b4\n"; } // "b4"를 출력하는 함수
};
// 클래스 B는 클래스 A를 public으로 상속
class B : public A {
// 클래스 B는 A의 기능을 사용할 수 있다.
};
int main() {
A aa; // A 클래스의 객체 aa 생성
B bb; // B 클래스의 객체 bb 생성
aa.b1(); // A 클래스의 public 멤버 함수 b1 호출
// 아래의 코드는 오류입니다. 접근할 수 없는 A 클래스의 private 멤버를 호출하려 하면 에러가 발생합니다.
// bb.a1(); // 오류: 'a1'은 private 멤버이므로 접근 불가
// bb.a2(); // 오류: 'a2'은 private 멤버이므로 접근 불가
return 0; // 프로그램 종료
}​

주석 설명 요약
기본 클래스 A: 사용자는 public 멤버(b1, b2, b3, b4)만 접근할 수 있으며, protected 멤버는 파생 클래스에서 접근 가능, private 멤버는 외부와 파생 클래스 모두 접근 불가.
파생 클래스 B: A를 public으로 상속받음을 보여주며, B는 A의 public 멤버에 접근할 수 있다.
main 함수: A와 B의 객체를 생성하고, A의 public 멤버를 호출하고 있음. B 객체는 A의 public 멤버에만 접근 가능.

 

 

 

# Java에서의 상속
Java는 클래스 기반 객체지향 언어로 상속을 `extends` 키워드를 사용하여 구현합니다.
```java
class Animal {
void eat() {System.out.println("Eating...");}
}
class Dog extends Animal {
void bark() {System.out.println("Barking...");}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 부모 클래스의 메소드 호출
dog.bark(); // 자식 클래스의 메소드 호출}
}
```
## PHP에서의 상속
PHP도 클래스 기반 언어로, `extends` 키워드를 통해 상속을 구현합니다.
```php
class Animal {
public function eat() {echo "Eating...\n";}
}
class Dog extends Animal {
public function bark() {echo "Barking...\n";}
}
$dog = new Dog();
$dog->eat(); // 부모 클래스의 메소드 호출
$dog->bark(); // 자식 클래스의 메소드 호출
```
## C에서의 상속
C는 객체지향 언어가 아니지만 구조체와 함수 포인터를 사용하여 비슷한 효과를 낼 수 있습니다. 그러나 진정한 상속 개념은 없습니다.
```c
#include <stdio.h>
#include <string.h>
struct Animal {char name[20];};
void eat() {printf("Eating...\n");}
struct Dog {struct Animal animal; // Animal 구조체 포함};
void bark() {printf("Barking...\n");}
int main() {
struct Dog dog;
strcpy(dog.animal.name, "Buddy");
printf("%s\n", dog.animal.name);
eat(); // 동물의 행동
bark(); // 개의 행동
return 0;
}
```
## C#에서의 상속
C# 역시 클래스 기반의 객체지향 언어로 `:` 기호를 사용하여 상속합니다.
```csharp
using System;
class Animal {
public void Eat() {
Console.WriteLine("Eating...");
}
}
class Dog : Animal {
public void Bark() {
Console.WriteLine("Barking...");
}
}
class Program {
static void Main() {
Dog dog = new Dog();
dog.Eat(); // 부모 클래스의 메소드 호출
dog.Bark(); // 자식 클래스의 메소드 호출
}
}
```
## Python에서의 상속
Python은 매우 직관적인 문법을 가지고 있으며, `(부모 클래스명)` 형태로 상속을 합니다.
```python
class Animal:
def eat(self):
print("Eating...")
class Dog(Animal):
def bark(self):
print("Barking...")
dog = Dog()
dog.eat() # 부모 클래스의 메소드 호출
dog.bark() # 자식 클래스의 메소드 호출
```
## C++에서의 상속
C++에서는 `:` 기호를 사용하여 상속하며, 접근제어 (public, protected, private)를 설정할 수 있습니다.
```cpp
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "Barking..." << endl;
}
};
int main() {
Dog dog;
dog.eat(); // 부모 클래스의 메소드 호출
dog.bark(); // 자식 클래스의 메소드 호출
return 0;
}

 

 

클래스 다이어그램 시험\\그림킽에서 위로 화살표, 용어암기

상속 표현 is a관계

아래는 객체지향 언어에서 부모 클래스와 자식 클래스에 대한 용어를 정리한 표입니다.

| 용어                                            | 설명                                                               
|--------------------------------------------|
| 부모 클래스 (Parent Class)         | 

다른 클래스가 상속받는 클래스로, 기본 클래스라고도 불립니다. 클래스의 공통적인 속성과 행동을 정의합니다. 
| 자식 클래스 (Child Class)           |

부모 클래스로부터 속성 및 메소드를 상속받는 클래스입니다. 파생 클래스라고도 불립니다.                
| 상속 (Inheritance)                       |

자식 클래스가 부모 클래스의 속성과 메소드를 물려받는 객체지향 프로그래밍의 핵심 개념입니다.        
| 파생 클래스 (Derived Class)       |

부모 클래스로부터 상속받아 생성된 클래스입니다.                                       
| 기본 클래스 (Base Class)           |

자식 클래스가 상속받는 클래스입니다. 부모 클래스를 지칭합니다.                               
| 슈퍼클래스 (Superclass)             |

부모 클래스를 다른 용어로 표현한 것으로, 자식 클래스의 상위 클래스입니다.                      
| 서브클래스 (Subclass)                |

자식 클래스를 다른 용어로 표현한 것으로, 부모 클래스를 확장하는 클래스입니다.                   
| 다중 상속 (Multiple Inheritance) | 

하나의 클래스가 여러 부모 클래스로부터 상속받는 경우입니다. 일부 언어(예: C++)에서 지원됩니다.       
| 오버라이드 (Override)                 | 

자식 클래스에서 부모 클래스의 메소드를 재정의하는 것입니다. 메소드를 수정하거나 대체할 수 있습니다. 
| 접근 제어 (Access Control)         |   

상속시 부모 클래스 속성의 접근 수준을 지정하는 것으로, public, protected, private으로 구분됩니다.       

 

 

부모 Animal클래스로부터 public으로 상속받은 자식 Dog클래스

 

강의 중 제일 중요한 표

상속 접근 제어는 부모 클래스의 멤버가 자식 클래스에서 어떻게 변환되는지를 설명합니다.
class Dog : public Animal이라는 선언은 Dog 클래스가 Animal 클래스를 public으로 상속받았음을 나타냅니다.

private: 부모 클래스에서 private으로 선언된 멤버는 자식 클래스에서 접근할 수 없습니다.
protected: 부모 클래스에서 protected 멤버는 자식 클래스에서는 protected으로 변환되며, 외부에서는 접근할 수 없습니다. 하지만 자식 클래스의 멤버 함수 내에서는 접근 가능합니다.
public: public으로 선언된 멤버는 자식 클래스에서도 public으로 남아 외부에서 접근할 수 있습니다.
3. 그림에서의 중요 포인트
상속 접근 제어의 역할: 이 표는 설계할 때 어떤 멤버를 자식 클래스가 사용할 수 있는지를 명확히 해줍니다. 각 접근 제어는 객체 지향 프로그래밍에서 캡슐화와 정보 은닉을 지원합니다.
예시 분석: class Dog : public Animal의 경우, Animal 클래스의 public 멤버는 Dog 클래스 내에서 public이 되고, protected 멤버는 protected가 됩니다. 그러나 private 멤버는 접근할 수 없습니다.

 

 

 

#include <iostream> // 표준 입출력 라이브러리 포함
using std::cout;  // cout을 std namespace에서 사용
using std::endl;  // endl을 std namespace에서 사용
class A // 기본 클래스 A 정의
{
    int x; // private 멤버 변수 x (기본 접근 제어는 private)
public:
    void setX(int i) { x = i; } // x에 값을 설정하는 공공 메서드
    void showX() { cout << x << endl; } // x의 값을 출력하는 공공 메서드
};
class B : public A // 클래스 B는 A를 public으로 상속함
{
    // 클래스 B는 부모 클래스 A의 멤버를 아무것도 추가하지 않음
    // 하지만 A의 public 멤버는 B의 멤버로 접근 가능함
};
int main() {
    A aa; // 기본 클래스 A의 객체 aa 생성
    aa.setX(1); // aa 객체의 x에 1을 설정
    aa.showX(); // aa의 x 값을 출력 (결과: 1)
    B bb; // 파생 클래스 B의 객체 bb 생성
    // bb.setX(10); // bb 객체에서 setX를 호출하려고 시도 (오류 발생)
    // bb.showX(); // bb 객체에서 showX를 호출하려고 시도 (오류 발생)
    return 0; // 프로그램 종료
}

 

 

 

 

 

 

자식만 접근 외부접근x

Private 상속의 개념
정의: 클래스 B가 클래스 A를 private로 상속할 경우, B는 A의 public과 protected 멤버를 상속받지만, B의 외부에서는 A의 멤버에 접근할 수 없습니다. 이는 B의 내부에서만 A의 기능을 사용하는 것을 의미합니다.
사용 사례
 1. 구현 세부 사항 은닉
상속의 숨김: private 상속은 클래스 B가 A로부터 상속받은 기능을 사용하면서도, A의 인터페이스를 B의 사용자에게 노출하고 싶지 않을 때 유용합니다.
예: B가 A라는 클래스를 사용하여 내부적으로 기능을 구현하더라도, 공개 API에서는 A의 멤버 함수를 그대로 노출하지 않도록 하는 경우입니다.
 2. 제한된 접근
접근 제어: B가 A의 멤버를 사용할 수 있지만, A의 기능을 사용할 수 있는 것은 오직 B의 멤버 함수에 한정됩니다. 즉, 사용자에게는 A의 멤버가 보이지 않게 하여 캡슐화의 원칙을 강화합니다.
 3. 특정 클래스와의 관계 설정
상속이 아닌 조합: B는 A의 성질을 가지지만, 사용자에게는 B가 A를 "사용"하는 식으로 느껴지도록 조정할 수 있습니다. 이를 통해 B는 A로부터 적절한 기능을 계승받으면서도, B의 초기 사용자에게는 A를 직접 사용할 수 없도록 강제합니다.

 

 

 

#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 문제로 냄

 

 

 

Protected 상속의 개념
정의: 클래스 B가 클래스 A를 protected로 상속할 경우, B는 A의 public 및 protected 멤버를 상속받습니다. 그러나 B의 외부에서는 A의 멤버에 접근할 수 없습니다. 대신, A의 protected 멤버는 B의 하위 클래스에서 접근할 수 있도록 허용됩니다.
사용 사례
 1. 하위 클래스에서의 접근 허용
하위 클래스의 접근: Protected 상속은 기본 클래스의 protected 멤버가 파생 클래스뿐만 아니라 그 자식 클래스에서도 접근할 수 있도록 하고 싶을 때 사용됩니다.
예: A라는 기본 클래스의 속성을 B와 그 하위 클래스에서만 사용하고, 외부에서 접근할 수 없게 하고 싶을 때 유용합니다.
 2. 구현 세부 사항의 은닉
코드 캡슐화: Protected 상속을 사용하면, B의 사용자는 A의 정보를 알 수 없으면서도, B의 하위 클래스에서는 A의 멤버에 쉽게 접근할 수 있습니다. 이를 통해 클래스 간의 결합도를 줄이고, 코드의 유지 보수성을 높일 수 있습니다.
 3. 다형성 활용
다형성 지원: 다형성을 사용하는 클래스 계층에서, 하위 클래스에 A의 protected 멤버를 제공하려는 경우에 적합합니다. 이를 통해 B에 추가된 기능이나 메서드에서 A의 멤버를 사용할 수 있게 만들어, 다형성을 극대화할 수 있습니다.

 

 

 

공통점
필드 보호: 두 접근 지정자 모두 클래스 외부에서는 접근할 수 없도록 설정되어 있습니다. 따라서, 외부 코드(자식 클래스 포함)에서 직접적으로 해당 멤버에 접근할 수 없습니다.
멤버 변수의 불변성: protected와 private 모두 클래스 내부에서만 해당 멤버 변수를 수정하거나 읽어들일 수 있습니다.

차이점
1. 접근 수준
Private:

클래스 내부와 파생 클래스에서만 접근이 가능합니다.
파생 클래스(자식 클래스)에서 private 멤버에 접근할 수 없습니다.

Protected:

클래스 내부뿐만 아니라 모든 파생 클래스에서도 접근할 수 있습니다.
다만, 클래스 외부에서는 접근할 수 없습니다.
cpp

2. 상속에서의 접근
Private 상속: 기본 클래스의 private 멤버는 상속받은 클래스에서도 접근할 수 없습니다.
Protected 상속: 이 경우 부모 클래스의 protected 및 public 멤버는 자식 클래스에서 직접 사용할 수 있습니다.
요약

Private: 외부 및 파생 클래스에서 접근 불가. 오직 기본 클래스 내부에서만 접근 가능.
Protected: 외부에서는 접근 불가하지만, 파생 클래스에서는 접근 가능.
이런 차이로 인해 protected는 계층 구조에서 클래스 간의 상호작용을 허용하므로 객체 지향 프로그래밍에서 유용하게 사용됩니다. private는 보다 엄격한 접근 제한을 제공하여 정보 은닉을 강화합니다.

 자식이 쉽게 부모의 것을 사용하기 위해서 protected로 만들어 사용한

#include <iostream>
using std::cout;
using std::endl;

class A {
protected: // protected 접근 지정자는 이 클래스와 파생 클래스에서 접근 가능함을 의미.
    int a, b; // A 클래스의 두 개의 정수형 변수 a와 b를 선언.
public:
    void setAB(int i, int j) { a = i; b = j; } // a와 b의 값을 설정하는 공용 메서드.
};

class B : public A { // B는 A를 public으로 상속받은 클래스.
    int c; // B 클래스만의 정수형 변수 c를 선언. 기본 접근 지정자는 private임.
public:
    void setC(int n) { c = n; } // c의 값을 설정하는 공용 메서드.
    void showABC() { cout << a << b << c << endl; } // a, b, c를 출력하는 메서드.
    // 기본 클래스의 protected 멤버들은
    // 파생 클래스인 B 내에서 접근이 가능함.
};

int main() {
    A aa; // A 클래스의 객체 aa를 생성.
    B bb; // B 클래스의 객체 bb를 생성.
    // aa.a; // 외부에서는 aa의 a에 접근할 수 없음. 주석 처리된 부분.
    // bb.b; // 외부에서는 bb의 b에 접근할 수 없음. 주석 처리된 부분.
    
    bb.setAB(1, 2); // bb 객체를 통해 A 클래스의 setAB() 메서드를 호출하여 a와 b를 설정.
    bb.setC(3); // bb 객체를 통해 c의 값을 설정.
    bb.showABC(); // bb 객체를 통해 a, b, c의 값을 출력.
    
    return 0; // 프로그램 종료.
}

클래스 A:

protected 변수 a와 b를 가지고 있으며, 이들은 오직 A와 이를 상속받은 자식 클래스 B에서 사용될 수 있습니다.
setAB() 메서드는 a와 b의 값을 지정합니다.
클래스 B:

클래스 A를 public으로 상속받으므로 A의 public 및 protected 멤버에 접근할 수 있습니다.
private 변수 c를 포함하고 있습니다. 이는 B 내부에서만 접근 가능합니다.
showABC() 메서드는 A로부터 상속받은 a와 b, B의 c 변수를 출력합니다.
main() 함수:

A와 B의 객체를 생성하고, B 객체에 a, b, c의 값을 설정한 후 출력합니다.
aa.a와 bb.b와 같은 접근은 주석 처리되어 있어, 외부에서 접근할 수 없음을 보여줍니다.

 

 

 

 

 

제일 많이 씀

#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;
}

호출순서

 

 

 

 

 

 

 

#include <iostream>
using std::cout;
using std::endl;

class A {
    int a; // A 클래스의 private 멤버 변수 a
public:
    // A의 생성자
    A(int i) {
        cout << "A의 생성자\n"; // 생성자 호출 시 메시지 출력
        a = i; // 매개변수 i로 a의 값을 초기화
    }
    // A의 소멸자
    ~A() { 
        cout << "A의 소멸자\n"; // 소멸자 호출 시 메시지 출력
    }
    // A의 멤버 함수
    void showA() { 
        cout << a << '\n'; // a의 값을 출력
    }
};

class B : public A { // A를 public으로 상속받은 B 클래스
    int b; // B 클래스의 private 멤버 변수 b
public:
    // B의 생성자
    B(int i, int j) : A(i) { // i는 부모 클래스 A를 초기화하는 데 사용됨
        cout << "B의 생성자\n"; // 생성자 호출 시 메시지 출력
        b = j; // 매개변수 j로 b의 값을 초기화
    }
    // B의 소멸자
    ~B() { 
        cout << "B의 소멸자\n"; // 소멸자 호출 시 메시지 출력
    }
    // B의 멤버 함수
    void showB() { 
        cout << b << endl; // b의 값을 출력
    }
};

int main() {
    B bb(10, 20); // B 클래스의 객체 bb를 생성, 이때 A 클래스의 생성자도 호출됨
    bb.showA(); // A 클래스의 showA() 메서드 호출
    bb.showB(); // B 클래스의 showB() 메서드 호출
    return 0; // 프로그램 종료
}
// 향상 시험 냄 이게 뭘하는 것니까?

코드 설명
클래스 A:

생성자:
A(int i)는 매개변수 i를 받아 a 값을 초기화합니다.
생성자 호출 시 "A의 생성자"를 출력합니다.
소멸자:
객체가 파괴될 때 수행되며, "A의 소멸자"라는 메시지를 출력합니다.
showA():
A 클래스의 a 값을 출력하는 메서드입니다.
클래스 B:

A 클래스를 상속받아 B라는 이름의 클래스입니다.
생성자:
B(int i, int j)는 부모 클래스 A(i)를 호출하여 i로 A의 a를 초기화합니다.
"B의 생성자"라는 메시지를 출력하고 b 값을 매개변수 j로 초기화합니다.
소멸자:
객체가 파괴될 때 "B의 소멸자"라는 메시지를 출력합니다.
showB():
B 클래스의 b 값을 출력하는 메서드입니다.
main() 함수:

B bb(10, 20);에서 B 클래스의 객체 bb를 생성합니다.
이때, B의 생성자가 호출되며, A의 생성자도 함께 호출됩니다.
cout을 통해 각각의 생성자 메시지를 출력합니다.
bb.showA();는 A 클래스의 showA() 메서드를 호출하여 a 값을 출력합니다.
bb.showB();는 B 클래스의 showB() 메서드를 호출하여 b 값을 출력합니다.
마지막으로, return 0;으로 프로그램을 종료하며, 이 과정 중에 소멸자들이 호출되어 소멸 메시지를 출력합니다.
실행 순서
생성자 호출 순서:
A의 생성자 호출
B의 생성자 호출
소멸자 호출 순서:
B의 소멸자 호출
A의 소멸자 호출

 

 

 

 

 

 

 

 

#include <iostream>
using std::cout;
using std::endl;

class A // 할아버지
{
    int a; // A 클래스의 private 멤버 변수 a
public:
    A(int i) { a = i; } // 생성자: 매개변수 i로 a를 초기화
    int getA() { return a; } // a 값을 반환하는 멤버 함수
};

class B : public A // 아버지
{
    int b; // B 클래스의 private 멤버 변수 b
public:
    B(int i, int j) : A(i) { // B의 생성자에서 A의 생성자를 호출
        // i는 기본 클래스 A의 생성자 매개변수로 전달됨
        b = j; // j로 b를 초기화
    }
    int getB() { return b; } // b 값을 반환하는 멤버 함수
};

class C : public B // 자식
{
    int c; // C 클래스의 private 멤버 변수 c
public:
    C(int i, int j, int k) : B(i, j) { // C의 생성자에서 B의 생성자를 호출
        // i, j는 클래스 B의 생성자 매개변수로 전달됨
        c = k; // k로 c를 초기화
    }
    void show() { // show라는 메서드를 정의
        cout << getA() << ' ' << getB() << ' ' << c << endl; // A, B, C의 값 출력
    }
};

int main()
{
    C cc(10, 20, 30); // C 객체를 생성하며 i, j, k에 각각 10, 20, 30 할당
    cc.show(); // C 객체의 show 메서드 호출
    cout << cc.getA() << ' ' << cc.getB() << endl; // A와 B의 값을 출력
    return 0; // 프로그램 종료
}

주석 설명
클래스 A:

할아버지: A 클래스의 역할을 비유적으로 표현하여 조상의 개념을 나타냅니다.
int a;: 클래스 A의 private 멤버 변수 a는 상태를 유지하는 데 사용됩니다.
A(int i): 생성자는 매개변수로 받은 i를 사용하여 a를 초기화합니다.
int getA(): a의 값을 반환하는 메서드입니다.
클래스 B:

아버지: B 클래스가 A로부터 상속받았음을 암시합니다.
B(int i, int j) : A(i): B의 생성자는 A의 생성자를 호출하며 i를 전달합니다. 이 구조는 상속 관계를 명확하게 나타냅니다.
주석: "// i는 기본 클래스 A의 생성자 매개변수로 전달됨"은 부모 클래스 A의 생성자가 호출되었음을 명확히 하고 있습니다. 이는 A의 멤버 a가 이 생성자를 통해 초기화된다는 것을 의미합니다.
b = j;: j를 통해 b를 초기화합니다.
int getB(): b의 값을 반환하는 메서드입니다.
클래스 C:

자식: C 클래스는 B 클래스에서 상속받아 "자식"의 개념을 강조합니다.
C(int i, int j, int k) : B(i, j): C의 생성자는 B의 생성자를 호출하며 i와 j를 전달합니다.
주석: "// i, j는 클래스 B의 생성자 매개변수로 전달됨"은 B의 생성자가 A를 초기화하는 과정을 강조합니다. 이는 C가 B 및 A의 속성을 모두 가질 수 있게 합니다.
c = k;: k를 통해 c를 초기화합니다.
void show(): A, B의 멤버 변수와 C의 멤버 변수를 출력하는 메서드입니다.
main 함수:

C cc(10, 20, 30);: C 클래스의 객체 cc를 생성하고 각각 10, 20, 30으로 A, B, C의 값을 초기화합니다.
cc.show();: C 클래스의 show 메서드를 호출하여 A, B, C의 상태를 출력합니다.
마지막으로 A와 B의 값을 따로 출력합니다.
결론
이 코드는 C++의 상속 구조와 생성자 호출 순서를 명확하게 보여줍니다. A에서 B로, B에서 C로 이어지는 상속 관계는 멤버 변수 접근 및 메서드 호출을 통해 잘 드러납니다

 

 

 

 

 

다중상속은 무제점들이 많아 잘 사용하지 않는다.

C++의 다중 상속(Multiple Inheritance)은 여러 개의 클래스를 동시에 상속받는 기능으로 코드 재사용성을 높이고 다양한 기능을 결합할 수 있는 장점이 있지만, 여러 문제점이 따릅니다. 다음은 C++의 다중 상속에서 발생할 수 있는 몇 가지 주요 문제점입니다.

### 1. 다이아몬드 문제(Diamond Problem)
- **정의**: A라는 클래스가 B와 C라는 두 클래스로부터 상속받고, B와 C가 다시 A를 상속받을 경우, D 클래스가 B와 C를 상속받을 때 어떤 A의 인스턴스를 사용할지 혼동을 일으킬 수 있는 문제입니다.
- **문제점**: 동일한 부모 클래스로부터 상속받은 두 개의 클래스가 자식 클래스에서 공유되기 때문에, 자식 클래스에서 부모 클래스의 속성을 사용할 때 어떤 부모의 속성을 참조하는 것인지 명확하지 않게 되며, 이는 코드의 예측 가능성을 떨어뜨립니다. 

```cpp
class A { public: int value; };
class B : public A {};
class C : public A {};
class D : public B, public C {};

D obj;
obj.value = 10; // 어떤 A의 value를 수정하는 건가?
```

### 2. 이름 충돌(Name Clashing)
- **정의**: 두 개 이상의 부모 클래스가 동일한 이름의 멤버 함수를 가지는 경우입니다.
- **문제점**: 자식 클래스에서 호출할 때 어떤 부모 클래스의 함수를 호출할지 명확하지 않아서 구체적인 클래스를 지정해야 합니다.

```cpp
class A { public: void func() {} };
class B { public: void func() {} };
class C : public A, public B {};

C obj;
obj.func(); // 오류: 어떤 func()를 호출할 것인지 불명확
```

### 3. 복잡성 증가
- **정의**: 클래스 및 상속 관계의 복잡성이 증가합니다.
- **문제점**: 유지보수가 어려워지고, 클래스 구조 및 인터페이스를 이해하기 위해 더 많은 시간이 필요합니다.

### 4. 성능 저하
- **정의**: 다중 상속을 사용하면 가상 함수 테이블(vtable) 구조가 덧붙여져 대량의 오버헤드가 발생할 수 있습니다.
- **문제점**: 메모리 사용량이 증가하고 메서드 호출 시 성능에 부담을 줄 수 있습니다.

### 대처 방법
- **가상 상속(Virtual Inheritance)**: 다이아몬드 문제를 해결하기 위한 방법으로, 부모 클래스를 가상으로 상속받아 단일 인스턴스만 존재하도록 합니다.

예시 코드:
```cpp
class A { public: int value; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
```
이 방식으로 다중 상속 간에 A 클래스의 중복을 피할 수 있습니다.

### 결론
C++의 다중 상속은 코드 재사용성을 높일 수 있지만 여러 문제점을 동반합니다. 이러한 단점을 이해하고 적절한 방법으로 대처하는 것이 중요합니다. 다중 상속을 사용하는 대신 인터페이스(순수 가상 클래스)를 사용하거나 조합(composition)과 같은 대체 디자인 패턴을 고려해 보는 것도 좋은 방법입니다. 

 

예제: 다중 상속 구현
상속받을 클래스를 정의하기

A1 클래스: 기본 클래스 1
A2 클래스: 기본 클래스 2
B 클래스: A1과 A2를 상속하는 파생 클래스

#include <iostream> // 입출력 스트림을 포함
using namespace std; // std 네임스페이스 사용

// 기본 클래스 A1 정의
class A1 {
    int a; // A1의 개인 멤버 변수
public:
    // 생성자: A1의 멤버 변수 a를 초기화
    A1(int i) : a(i) {}

    // A1의 값을 반환하는 메서드
    int getA() { return a; }
};

// 기본 클래스 A2 정의
class A2 {
    int b; // A2의 개인 멤버 변수
public:
    // 생성자: A2의 멤버 변수 b를 초기화
    A2(int i) : b(i) {}

    // A2의 값을 반환하는 메서드
    int getB() { return b; }
};

// 클래스 B: A1과 A2를 다중 상속받는 파생 클래스
class B : public A1, public A2 {
    int c; // B의 개인 멤버 변수
public:
    // 생성자: A1과 A2의 생성자를 호출하여 초기화
    // i는 A1의 생성자에 전달되고, j는 A2의 생성자에 전달됨
    B(int i, int j, int k) : A1(i), A2(j), c(k) {}

    // 결과를 출력하는 메서드
    void show() {
        cout << "A1의 값: " << getA() << endl; // A1의 값 출력
        cout << "A2의 값: " << getB() << endl; // A2의 값 출력
        cout << "B의 값: " << c << endl; // B의 값 출력
    }
};

// 프로그램의 진입점
int main() {
    // B 클래스의 객체 생성
    // A1 생성자에 1, A2 생성자에 2, B의 c 값으로 3을 전달
    B bb(1, 2, 3);
    
    // show 메서드를 호출하여 결과 출력
    bb.show();
    return 0; // 프로그램 종료
}

 

class B :public A1, public A2 떄

class B :public A2, public A1 떄

 

 

 

 

#include <iostream>
using std::cout;
using std::endl;
using std::string;
class Man{
protected:
	int age;
	string name;
public:
	Man(string n, int a) {
		name = n;
		age = a;
	}
	void m_show() {
		cout << "이름:" << name << endl;		
		cout << "나이:" << age << endl;
	}
};}//부모
int main()
{
	//Student kks("김컴소", 20, "C반", "202012000");
	//Teacher hsh("한미소", 40, "전산", "C++프로그래밍");
	//kks.s_show();
	//hsh.t_show();
	return 0;
}
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class Man {
protected:
	int age;
	string name;
public:
	Man(string n, int a) {
		name = n;
		age = a;
	}
	void m_show() {
		cout << "이름:" << name << endl;
		cout << "나이:" << age << endl;
	}
};
class Student : public Man {
	string ban;
	string hak;
public:
	Student(string n, int a, string b, string h) :Man(n, a) {
		ban = b;
		hak = h;
	}
	void s_show() {
		m_show
		cout << "반:" << ban << endl;
		cout << "학번:" << hak << endl;
	}
};
int main()
{
	Student kks("김컴소", 20, "C반", "202012000");
	//Teacher hsh("한미소", 40, "전산", "C++프로그래밍");
	kks.s_show();
	//hsh.t_show();
	return 0;
}
d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <iostream> // iostream 라이브러리 포함
using namespace std; // std 네임스페이스 사용

// 기본 클래스 Man 정의
class Man {
protected: // 보호 멤버
    string name; // 이름
    int age;     // 나이
public:
    // 생성자, name과 age를 초기화
    Man(string n, int a) {
        name = n; // name 초기화
        age = a;   // age 초기화
    }
  
    // Man의 정보를 출력하는 메서드
    void m_show() {
        cout << "이름: " << name << endl; // 이름 출력
        cout << "나이: " << age << endl;   // 나이 출력
    }
};

// Student 클래스: Man 클래스를 상속받음
class Student : public Man {
    string ban; // 반
    string hak; // 학과
public:
    // 생성자, ban과 hak을 초기화
    Student(string n, int a, string b, string h) : Man(n, a) {
        ban = b; // 반 초기화
        hak = h; // 학과 초기화
    }

    // Student의 정보를 출력하는 메서드
    void s_show() {
        m_show(); // Man의 메서드 호출
        cout << "반: " << ban << endl; // 반 출력
        cout << "학과: " << hak << endl; // 학과 출력
    }
};

// Teacher 클래스: Man 클래스를 상속받음
class Teacher : public Man {
    string major;  // 전공
    string subject; // 과목
public:
    // 생성자, major와 subject를 초기화
    Teacher(string n, int a, string m, string s) : Man(n, a) {
        major = m; // 전공 초기화
        subject = s; // 과목 초기화
    }

    // Teacher의 정보를 출력하는 메서드
    void t_show() {
        m_show(); // Man의 메서드 호출
        cout << "전공: " << major << endl; // 전공 출력
        cout << "담당 과목: " << subject << endl; // 과목 출력
    }
};

// 메인 프로그램
int main() {
    // Student 객체 생성
    Student s("홍길동", 20, "1반", "컴퓨터공학");
    s.s_show(); // Student 정보 출력

    // Teacher 객체 생성
    Teacher t("이순신", 35, "역사", "한국사");
    t.t_show(); // Teacher 정보 출력

    return 0; // 프로그램 종료
}

Man 클래스: 이름과 나이를 저장하고 정보를 출력하는 기본 클래스.
Student 클래스: Man을 상속받아 반과 학과를 추가로 포함하며 정보를 출력.
Teacher 클래스: Man을 상속받아 전공과 과목을 추가로 포함하며 정보를 출력.
main 함수: 각각의 객체인 Student과 Teacher를 생성하고 정보를 출력.

'인덕대 C++-출처 smile han' 카테고리의 다른 글

13주  (0) 2024.11.25
13주차 예습  (0) 2024.11.24
12주차 예습  (0) 2024.11.17
c++11주차  (0) 2024.11.11
11주차예습  (0) 2024.11.10