반응형
상속의 기본 (1)
Inheritance(is-a relationship)
#include <iostream>
using namespace std;
class Mother // generalized class
{
private: // 자식한테도 허용X
//public: // 다 열어 버림
//protected: // private 상태를 유지하면서 자식에게는 허용
int m_i;
public:
Mother(const int & i_in)
: m_i(i_in)
{
std::cout << "Mother constructor" << std::endl;
}
void setValue(const int& i_in)
{
m_i = i_in;
}
int getValue()
{
return m_i;
}
};
// Mother class로부터 많은 클래스를 유도해서 만들어 낼 수 있음
// Child class is derived from Mother class
class Child : public Mother // derived class
{
private:
double m_d;
public:
Child(const int & i_in, const double & d_in)
//: m_i(i_in), m_d(d_in)
: Mother(i_in), m_d(d_in)
{
// Child의 생성자가 호출될 경우, Mother의 생성자를 같이 호출함!!
/*Mother::setValue(i_in);
m_d = d_in;*/
}
void setValue(const int & i_in, const double & d_in)
{
Mother::setValue(i_in);
m_d = d_in;
}
void setValue(const double & d_in)
{
m_d = d_in;
}
double getValue()
{
return m_d;
}
};
class Daughter : public Mother
{
};
class Son : public Mother
{
};
int main()
{
Mother mother(1024);
//mother.setValue(1024);
cout << mother.getValue() << endl;
Child child(1024, 128);
// 따로 구현하지 않아도 Mother 클래스의 것들 그대로 사용 가능!
// => Child 클래스에서 구현하면 Child의 것을 우선으로 부름
/*child.setValue(128);
child.Mother::setValue(1024);*/
cout << child.Mother::getValue() << endl;
cout << child.getValue() << endl;
return 0;
}
상속의 기본 (2)
Student.h
#pragma once
#include <iostream>
#include <string>
class Student
{
private:
std::string m_name;
int m_intel; // intelligence;
public:
Student(const std::string & name_in = "No Name", const int & intel_in = 0)
: m_name(name_in), m_intel(intel_in)
{}
void setName(const std::string & name_in)
{
m_name = name_in;
}
std::string getName()
{
return m_name;
}
void setIntel(const int & intel_in)
{
m_intel = intel_in;
}
int getIntel()
{
return m_intel;
}
friend std::ostream & operator << (std::ostream & out, const Student & student)
{
out << student.m_name << " " << student.m_intel;
return out;
}
};
Teacher.h
#pragma once
#include <string>
class Teacher
{
private:
std::string m_name;
public:
Teacher(const std::string & name_in = "No Name")
: m_name(name_in)
{}
void setName(const std::string & name_in)
{
m_name = name_in;
}
std::string getName()
{
return m_name;
}
friend std::ostream & operator << (std::ostream & out, const Teacher & teacher)
{
out << teacher.m_name;
return out;
}
};
학생과 선생을 "사람"으로 묶어 일반화한다.
Student is a Person
Teacher is a Person
다른 사람이 봤을 때도 납득이 갈 만한 클래스 설계가 필요하다.
Person.h
#pragma once
#include <string>
#include <iostream>
class Person
{
private:
// 헤더 파일에서는 using namespace std 쓰지 않는 편이 좋음
// 현재 Student/Teacher에서 m_name에 접근하려면 error
// 1. m_name을 관리하는 클래스는 "Person"임
// 2. Student가 생성되는 시점에서는 m_name이 생성되어 있지 않음
// private라서 자식의 getter/setter에서 error! => public으로 바꾸면?
// 어디에서 m_name에 값에 접근하고 변경하는지 알기 힘들어짐
std::string m_name;
public:
//Person()
// : m_name("No Name")
//{}
// m_name을 초기화하는 생성자 생성
Person(const std::string & name_in = "No Name")
: m_name(name_in)
{}
void setName(const std::string & name_in)
{
m_name = name_in;
}
// const 없으면 error!
std::string getName() const
{
return m_name;
}
void doNothing() const
{
std::cout << m_name << " is doing nothing " << std::endl;
}
};
Student.h
#pragma once
//#include <string>
#include "Person.h"
class Student : public Person
{
private:
//std::string m_name;
int m_intel; // intelligence;
public:
Student(const std::string & name_in = "No Name", const int & intel_in = 0)
//: m_name(name_in), m_intel(intel_in)
: Person(name_in), m_intel(intel_in)
{}
void setIntel(const int & intel_in)
{
m_intel = intel_in;
}
int getIntel()
{
return m_intel;
}
void study()
{
std::cout << getName() << " is studying " << std::endl;
}
friend std::ostream & operator << (std::ostream & out, const Student & student)
{
//out << student.m_name << " " << student.m_intel;
out << student.getName() << " " << student.m_intel;
return out;
}
};
Teacher.h
#pragma once
//#include <string>
#include "Person.h"
class Teacher : public Person
{
private:
//std::string m_name;
public:
Teacher(const std::string & name_in = "No Name")
: Person(name_in)
{
this->getName(); // 부모 클래스의 함수 접근 가능
}
void teach()
{
std::cout << getName() << " is teaching " << std::endl;
}
friend std::ostream & operator << (std::ostream & out, const Teacher & teacher)
{
//out << teacher.m_name; // error!
out << teacher.getName();
return out;
}
};
main.cpp
#include "Student.h"
#include "Teacher.h"
int main()
{
// #include <string>이 없지만, Person.h에 되어 있기 때문에 간접적으로 사용 가능
std::string name;
Student std("Jack Jack");
std.setName("Jack Jack 2");
std.getName();
std::cout << std.getName() << std::endl;
Teacher teacher1("Dr. H");
teacher1.setName("Dr. K");
std::cout << teacher1.getName() << std::endl;
std::cout << std << std::endl;
std::cout << teacher1 << std::endl;
std.doNothing();
teacher1.doNothing();
std.study();
//std.teach();
teacher1.teach();
//teacher1.study();
Person person;
person.setName("Mr. Incredible");
person.getName();
//person.study();
//person.teach();
return 0;
}
유도된 클래스들의 생성 순서
#include <iostream>
using namespace std;
class Mother
{
public:
int m_i;
/*
Mother()
: m_i(1)
{
cout << "Mother construction" << endl;
}
*/
// 기본 값을 넣으면, 기본 생성자 Mother() 따로 구현할 필요 X
Mother(const int & i_in = 0)
: m_i(i_in)
{
cout << "Mother construction" << endl;
}
};
class Child : public Mother
{
private:
double m_d;
public:
Child()
//: m_i(1024) // error!
: m_d(1.0), Mother(1024) // 접근 순서: Mother() -> Child()
{
//부모 클래스 생성자에서 먼저 초기화한 후 m_i에 접근 가능함!! (public의 경우)
this->m_i;
this->Mother::m_i;
cout << "Child construction" << endl;
}
};
int main()
{
Child c1;
return 0;
}
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A constructor" << endl;
}
};
class B : public A
{
public:
B()
{
cout << "B constructor" << endl;
}
};
class C : public B
{
public:
C()
{
cout << "C constructor" << endl;
}
};
int main()
{
C c;
cout << endl;
B b;
cout << endl;
A a;
return 0;
}
유도된 클래스들의 생성과 초기화
#include <iostream>
using namespace std;
class Mother
{
private:
int m_i;
public:
Mother(const int & i_in = 0)
: m_i(i_in)
{
cout << "Mother construction" << endl;
}
};
class Child : public Mother
{
private:
double m_d;
public:
Child()
: m_d(1.0), Mother(1024)
{
//this->m_i;
//this->Mother::m_i;
cout << "Child construction" << endl;
}
};
int main()
{
Child c1;
cout << sizeof(Mother) << endl;
cout << sizeof(Child) << endl; // Mother의 int m_i 상속받음! padding 찾아보기
return 0;
}
#include <iostream>
using namespace std;
class A
{
public:
A(int a)
{
cout << "A: " << a << endl;
}
~A()
{
cout << "Destructor A" << endl;
}
};
class B : public A
{
public:
B(int a, double b)
: A(a)
{
cout << "B: " << b << endl;
}
~B()
{
cout << "Destructor B" << endl;
}
};
class C : public B
{
public:
C(int a, double b, char c)
: B(a, b)
{
cout << "C: " << c << endl;
}
~C()
{
cout << "Destructor C" << endl;
}
};
int main()
{
C c(1024, 3.14, 'A');
// 소멸자에서 동적 할당받은 메모리를 지워야 하는 상황이라면
// C에만 해당되는 걸 지우고, B에만 해당되는 걸 지우고... => 역순으로!
return 0;
}
상속과 접근 지정자
#include <iostream>
using namespace std;
class Base
{
public:
int m_public;
protected:
int m_protected;
private:
int m_private;
};
class Derived : public Base
{
public:
Derived()
{
m_public = 123; // == this->m_public, Base::m_public
m_protected = 123;
//m_private = 123;
}
};
class Derived2 : protected Base
{
public:
Derived2()
{
m_public = 123;
m_protected = 123;
//m_private = 123;
}
};
// private으로 상속받는 경우, 상속받은 클래스의 자식 클래스(GrandChild)에서 Base에서 받은 변수에 접근 불가능!!
class Derived3 : private Base
{
public:
Derived3()
{
m_public = 123;
m_protected = 123;
//m_private = 123;
}
};
class GrandChild : public Derived3
{
public:
GrandChild()
{
//m_public = 123;
//m_protected = 123;
//m_private = 123;
}
};
int main()
{
Base base;
base.m_public = 123;
//base.m_protected = 123;
//base.m_private = 123;
Derived d;
d.m_public = 123;
//d.m_protected = 123;
//d.m_private = 123;
Derived2 d2;
//d2.m_public = 123; // 외부에서 접근 불가능!!
//d2.m_protected = 123;
//d2.m_private = 123;
return 0;
}
유도된 클래스에 새로운 기능 추가하기
#include <iostream>
using namespace std;
class Base
{
protected:
int m_value;
public:
Base(int value)
: m_value(value)
{}
};
class Derived : public Base
{
public:
Derived(int value)
: Base(value)
{}
void setValue(int value)
{
Base::m_value = value;
// do some work with the variables defined in Derived
}
};
int main()
{
return 0;
}
상속받은 함수를 오버라이딩하기
overriding: 이름이 같은 함수를 자식 클래스에서 새로 정의해서, 기능을 더 추가해서 사용하는 것
#include <iostream>
using namespace std;
class Base
{
private:
int m_i;
public:
Base(int value)
: m_i(value)
{}
void print()
{
cout << "I'm base" << endl;
}
friend std::ostream & operator << (std::ostream & out, const Base &b)
{
out << "This is base output" << endl;
return out;
}
};
class Derived : public Base
{
private:
double m_d;
public:
Derived(int value)
: Base(value)
{}
void print()
{
Base::print();
//print(); => 주의! 무한 반복!!
cout << "I'm derived" << endl;
}
friend std::ostream & operator << (std::ostream & out, const Derived &b)
{
//Base::operator << ???
out << static_cast<Base>(b); // Derived는 Base+추가 메모리를 가진 형태라서 이러한 접근도 가능!
out << "This is derived output" << endl;
return out;
}
};
int main()
{
Base base(5);
base.print();
cout << base;
cout << endl;
Derived derived(7);
derived.print();
cout << derived;
return 0;
}
상속받은 함수를 감추기
부모 클래스로부터 상속받은 함수의 사용을 막고 싶을 때
1. private: using Base::print; - 이때, ()를 제외한 함수 이름만 적음
2. private: void print() = delete;
#include <iostream>
using namespace std;
class Base
{
protected:
int m_i;
public:
Base(int value)
: m_i(value)
{}
void print()
{
cout << "I'm base" << endl;
}
};
class Derived : public Base
{
private:
double m_d;
using Base::print; // do not add ()!
public:
Derived(int value)
: Base(value)
{}
// public 아래에 해당 코드를 치면, Base::m_i가 Derived 안에서는 public이 됨
using Base::m_i;
};
class Derived2 : public Base
{
private:
void print() = delete;
};
int main()
{
Derived derived(7);
//derived.m_i = 1024; // protected이기 때문에 클래스 외부에서 접근 error!
// 단, public: using Base::m_i 후에 주석을 지워 보면 접근이 가능함!!
Base base(5);
base.print(); // 가능
//derived.print(); // private: using Base::print; 후에 주석을 지워 보면 호출 불가능함!!
Derived2 derived2();
//derived2.print(); // delete했기 때문에 호출 불가능!
return 0;
}
다중 상속
다중 상속을 잘못 사용하면 다이아몬드 상속 구조가 될 수도 있다!! 주의하기
#include <iostream>
using namespace std;
class USBDevice
{
private:
long m_id;
public:
USBDevice(long id) : m_id(id) {}
long getID() { return m_id; }
void plugAndPlay() {}
};
class NetworkDevice
{
private:
long m_id;
public:
NetworkDevice(long id) : m_id(id) {}
long getID() { return m_id; }
void networking() {}
};
class USBNetworkDevice : public USBDevice, public NetworkDevice
{
public:
USBNetworkDevice(long usb_id, long net_id)
: USBDevice(usb_id), NetworkDevice(net_id)
{}
USBNetworkDevice(long id)
: USBDevice(id), NetworkDevice(id)
{}
};
int main()
{
USBNetworkDevice my_device(3.14, 6.022);
my_device.networking();
my_device.plugAndPlay();
// 문제는 중복되는 함수! => 어디서 받은 getID()를 호출할 것인가?
//my_device.getID();
my_device.USBDevice::getID();
my_device.NetworkDevice::getID();
return 0;
}
해당 포스트는 '홍정모의 따라하며 배우는 C++' 강의를 수강하며 개인 백업용으로 메모하였습니다.
반응형