profile image

L o a d i n g . . .

반응형

상속의 기본 (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++' 강의를 수강하며 개인 백업용으로 메모하였습니다.

인프런: https://www.inflearn.com/course/following-c-plus

 

홍정모의 따라하며 배우는 C++ - 인프런

만약 C++를 쉽게 배울 수 있다면 배우지 않을 이유가 있을까요? 성공한 프로그래머로써의 경력을 꿈꾸지만 지금은 당장 하루하루 마음이 초조할 뿐인 입문자 분들을 돕기 위해 친절하고 자세하게 설명해드리는 강의입니다. 초보로 시작하더라도 중급을 넘어 고급 프로그래머로 가는 길목에 들어서고 싶으시다면 최고의 디딤돌이 되어드리겠습니다. 여러분의 꿈을 응원합니다! 초급 프로그래밍 언어 C++ 온라인 강의 C++

www.inflearn.com

반응형
복사했습니다!