C++ 13주차
상속 (inheritance)
protected와 private의 공통점과 차이점
공통점:
1. protected와 private 모두 클래스 내부에서 선언된 멤버에 접근할 수 있도록 제한을 둡니다.
2. 두 접근 속성은 외부에서 직접 접근할 수 없으며, 클래스의 메서드를 통해서만 접근할 수 있습니다.
3. protected와 private 멤버는 상속을 통해 파생 클래스에서 접근할 수 있습니다.
차이점:
1. private 멤버는 해당 멤버를 선언한 클래스 내부에서만 접근할 수 있습니다. 즉, 외부에서는 접근할 수 없습니다. 이는 클래스의 내부 구현 세부 사항을 숨기고, 외부로부터의 직접 접근을 제한하기 위해 사용됩니다.
class Example {
private:
int privateVar; // private 멤버 변수
protected:
int protectedVar; // protected 멤버 변수
public:
void setPrivateVar(int value) {
privateVar = value;
}
int getPrivateVar() {
return privateVar;
}
};
class Derived : public Example {
public:
void accessProtectedVar() {
protectedVar = 10; // protected 멤버에 접근 가능
}
};
int main() {
Example obj;
obj.setPrivateVar(5); // private 멤버에 접근 가능
Derived derivedObj;
derivedObj.accessProtectedVar(); // protected 멤버에 접근 가능
return 0;
}
실습 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; }
};
▶ 자식쪽에서 부모쪽의 생성자에 매개변수를 넘기는 방법:
파생클래스생성자(매개변수 리스트) :기본클래스생성자(매개변수리스트) {
:
}
상속(inheritance) 심화
다중상속을 지원하는 프로그래밍 언어
1. 파이썬 (Python): 파이썬은 다중 상속을 지원하는 대표적인 언어입니다. 클래스 정의 시에 여러 부모 클래스를 지정하여 다중 상속을 구현할 수 있습니다.
class Parent1:
def method1(self):
print("Parent1의 메서드")
class Parent2:
def method2(self):
print("Parent2의 메서드")
class Child(Parent1, Parent2):
pass
# Child 클래스는 Parent1과 Parent2 클래스를 다중 상속받습니다.
child = Child()
child.method1() # Parent1의 메서드
child.method2() # Parent2의 메서드
2. C++: C++ 역시 다중 상속을 지원하는 언어입니다. 클래스 정의 시에 쉼표로 구분하여 여러 부모 클래스를 지정할 수 있습니다. 다중 상속을 사용할 때는 다이아몬드 상속 등 주의해야 할 점이 있으므로 주의가 필요합니다.
3. Eiffel: Eiffel 언어는 다중 상속을 지원하는 객체 지향 프로그래밍 언어입니다. Eiffel에서는 여러 부모 클래스로부터 상속받을 수 있습니다.
4. Perl: Perl은 다중 상속을 지원하는 스크립트 언어입니다. 여러 부모 클래스로부터 상속받을 수 있으며, 다중 상속의 유연성을 활용하여 다양한 기능을 구현할 수 있습니다.
실습 8-10: 2개의 기본 클래스 상속 1
#include <iostream>
using std::cout;
using std::endl;
class A1 // 아버지
{
int a;
public:
A1(int i) { a = i; }
int getA() { return a; }
};
class A2 // 어머니
{
int b;
public:
A2(int i) { b = i; }
int getB() { return b; }
};
class B :public A1, public A2
{
// 기본 클래스 A1과 A2로부터
// 상속 받은 파생 클래스
int c;
public:
B(int i, int j, int k) :A1(i), A2(j) { c = k; }
// i는 기본클래스 A1의 생성자로,
// j는 기본클래스 A2의 생성자로
// 각각 매개변수 전달
void show() {
cout << getA() << ' ' << getB() << ' ' << c << endl;
}
};
int main()
{
B bb(1, 2, 3);
bb.show();
return 0;
}
실습 8-11: 2개의 기본 클래스 상속 2
#include <iostream>
using std::cout;
using std::endl;
class B1 { //아버지
double d;
public:
B1(double dd) { d = dd; }
double getD() { return d; }
};
class B2 { //어머니
int i;
public:
B2(int ii) { i = ii; }
int getI() { return i; }
};
class D :public B1, public B2 {
char c;
public:
D(double dd, int ii, char cc) :B1(dd), B2(ii)
{
c = cc;
}
void print() {
cout << "Double : " << getD() << endl;
cout << "Int : " << getI() << endl;
cout << "Char : " << c << endl;
}
};
int main()
{
D d(1.23, 10, 'H');
cout << d.getD() << ',' << d.getI() << endl;
d.print();
return 0;
}
실습 8-12: 여러 개의 기본 클래스를 상속 받을 때, 생성자와 소멸자의실행 순서
#include <iostream>
using std::cout;
class A1 // 기본 클래스 1
{
int a;
public:
A1() { cout << "A1의 생성자.\n"; }
~A1() { cout << "A1의 소멸자.\n"; }
};
class A2 // 기본 클래스 2
{
int b;
public:
A2() { cout << "A2의 생성자.\n"; }
~A2() { cout << "A2의 소멸자.\n"; }
};
class B : public A1, public A2
// 기본 클래스 1과 2로부터
// 상속 받은 파생 클래스
{
int c;
public:
B() { cout << "B의 생성자.\n"; }
~B() { cout << "B의 소멸자.\n"; }
};
int main()
{
B bb;
return 0;
}
▶ 생성자는 순서대로 호출이 외고 소멸자는 역순으로 호출한다.
실습 8-13: 이름과 전화번호를 관리(기본클래스 Name, 파생클래스Phone)
#include <iostream>
using std::cout;
#include <string>
class Name { //기본 클래스는 이름만 처리
std::string name;
public:
void get_name(std::string s) { name = s; }
void print_name() { cout << name << "의 전화번호는"; }
};
class Phone : public Name { //파생클래스는 전화번호처리
std::string phone;
public:
void get_phone(std::string s) { phone = s; }
void print_phone() {
print_name();
cout << phone;
}
};
int main()
{
Phone h;
h.get_name("Smile Han");
h.get_phone("1234-5678");
h.print_phone();
return 0;
}
과제 par1.
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class Man {
string name;
int age;
public:
Man(string name, int age) { //생성자
this->name = name;
this->age = age;
}
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;
}
▶ 부모 클래스 완성
self와 this:
'self' 와 'this'는 프로그래밍에서 객체 지향 프로그래밍(OOP)의 핵심 요소인 인스턴스 메소드에서 현재 인스턴스를 참조하는 키워드
1.Python 'self'
class TestClass:
def __init__(self, name):
self.name = name
def printName(self):
print(self.name)
test = TestClass('루튼')
test.printName() # '루튼' 출력
2. javaScript 'this'
class TestClass {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
const test = new TestClass('루튼');
test.printName(); // '루튼' 출력
과제 part2.
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class Man {
string name;
int age;
public:
Man(string name, int age) { //생성자
this->name = name;
this->age = age;
}
void m_show() { //출력하는 함수
cout << "이름: " << name << endl;
cout << "나이: " << age << endl;
}
};
class Student :public Man {
string ban;
string hak;
public:
Student(string n, int a, string ban, string hak) : Man(n, a){
this->ban = ban;
this->hak = hak;
}
void s_show() {
m_show();
cout << "반: " << ban << endl;
cout << "학번: " << hak << endl << 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
string name;
int age;
public:
Man(string name, int age) { //생성자
this->name = name;
this->age = age;
}
void m_show() { //출력하는 함수
cout << "이름: " << name << endl;
cout << "나이: " << age << endl;
}
};
class Student :public Man {
string ban;
string hak;
public:
Student(string n, int a, string ban, string hak) : Man(n, a){
this->ban = ban;
this->hak = hak;
}
void s_show() {
m_show();
cout << "반: " << ban << endl;
cout << "학번: " << hak << endl << endl;
}
};
class Teacher :public Man {
string major;
string subject;
public:
Teacher(string n, int a, string major, string subject) :Man(n, a) {
this->major = major;
this->subject = subject;
}
void t_show() {
m_show();
cout << "전공: " << major << endl;
cout << "담당과목: " << subject << endl;
}
};
int main()
{
Student kks("김컴소", 20, "C반", "202012000");
Teacher hsh("한미소", 40, "전산", "C++프로그래밍");
kks.s_show();
hsh.t_show();
return 0;
}
과제 C++ → python, java
class Man:
def __init__(self, name, age):
self.name = name
self.age = age
def m_show(self):
print(f"이름: {self.name}")
print(f"나이: {self.age}")
class Student(Man):
def __init__(self, name, age, ban, hak):
super().__init__(name, age)
self.ban = ban
self.hak = hak
def s_show(self):
self.m_show()
print(f"반: {self.ban}")
print(f"학번: {self.hak}\n")
class Teacher(Man):
def __init__(self, name, age, major, subject):
super().__init__(name, age)
self.major = major
self.subject = subject
def t_show(self):
self.m_show()
print(f"전공: {self.major}")
print(f"담당과목: {self.subject}")
kks = Student("김컴소", 20, "C반", "202012000")
hsh = Teacher("한미소", 40, "전산", "C++프로그래밍")
kks.s_show()
hsh.t_show()
class Man {
protected String name;
protected int age;
public Man(String name, int age) {
this.name = name;
this.age = age;
}
public void m_show() {
System.out.println("이름: " + name);
System.out.println("나이: " + age);
}
}
class Student extends Man {
private String ban;
private String hak;
public Student(String name, int age, String ban, String hak) {
super(name, age);
this.ban = ban;
this.hak = hak;
}
public void s_show() {
m_show();
System.out.println("반: " + ban);
System.out.println("학번: " + hak);
}
}
class Teacher extends Man {
private String major;
private String subject;
public Teacher(String name, int age, String major, String subject) {
super(name, age);
this.major = major;
this.subject = subject;
}
public void t_show() {
m_show();
System.out.println("전공: " + major);
System.out.println("담당과목: " + subject);
}
}
public class Main {
public static void main(String[] args) {
Student kks = new Student("김컴소", 20, "C반", "202012000");
Teacher hsh = new Teacher("한미소", 40, "전산", "C++프로그래밍");
kks.s_show();
hsh.t_show();
}
}
overriding:가상함수(virtual function)
static
오버로딩(Overloading) vs 오버라이딩(Overriding)
1. 오버로딩(Overloading): 오버로딩은 같은 이름의 메소드가 여러 개 있지만, 매개변수의 타입이나 개수가 다른 경우를 말합니다. 이를 통해 같은 기능을 하는 메소드에 대해 동일한 이름을 사용할 수 있습니다. 매개변수의 타입이나 개수에 따라 자동으로 적절한 메소드를 선택하여 실행합니다.
2. 오버라이딩(Overriding): 오버라이딩은 상위 클래스에서 이미 정의된 메소드를 하위 클래스에서 재정의하여 사용하는 것을 말합니다. 메소드의 이름, 매개변수의 타입 및 개수, 반환 타입이 모두 동일해야 하며, 하위 클래스의 메소드에서 상위 클래스의 메소드를 덮어씁니다. 이를 통해 상속받은 메소드의 기능을 변경하거나 확장할 수 있습니다.
오버라이딩: 가상함수 구현
실습 9 -6: virtual 있을 때와 없을 때의 차이
#include <iostream>
using std::cout;
class Dot {
public:
void draw() {cout << "Dot::draw()\n";}
void print() {
cout << "Dot 클래스\n";
draw();
}
};
class Line :public Dot {
public:
void draw() { cout <<"Line::draw()\n";}
};
int main() {
Line line;
line.print();
return 0;
}
#include <iostream>
using std::cout;
class Dot {
public:
virtual void draw() {
cout << "Dot::draw()\n";
}
void print() {
cout << "Dot 클래스\n";
draw();
}
};
class Line :public Dot {
public:
void draw() override {
cout <<"Line::draw()\n";
}
};
int main() {
Line line;
line.print();
return 0;
}
▶ 자식쪽에서 override는 써도되고 안써도 된다. 부모쪽에서는 반드시 함수 앞에 virtual을 써야 한다.
출처: c++ 프로그래밍 강의자료