profile image

L o a d i n g . . .

반응형

객체들의 관계

1. 프로그램이 수행해야 하는 기능 정하기
2. 어떤 객체들이 어떻게 나눠서 도움을 주고받을지 설계
3. 설계에 따라서 여러 클래스를 구현

 

구성(요소) 관계

Composition

  • Part-of
  • 두뇌는 육체의 일부이다. 육체 없이는 존재할 수 없으며, 두뇌가 육체 전체에 대해 알고 있지는 않다.
  • 전체/부품 - 육체/두뇌
  • 다른 클래스에도 속할 수 없음 - 두뇌는 다른 클래스에 속할 수 없음
  • 멤버의 존재를 클래스가 관리함 - 두뇌를 육체가 관리함
  • 단방향

 

Position2D.h

#pragma once
#include <iostream>

class Position2D
{
private:
	int m_x;
	int m_y;

public:
	Position2D(const int & x_in, const int & y_in)
		: m_x(x_in), m_y(y_in)
	{}

	//TODO: overload =

	void set(const Position2D & pos_target)
	{
		set(pos_target.m_x, pos_target.m_y);
		// m_x = pos_target.m_x;
		// m_y = pos_target.m_y;
		// => 아래와 기능이 겹치기 때문에 함수를 호출하는 것이 좋음
	}

	void set(const int & x_target, const int & y_target)
	{
		m_x = x_target;
		m_y = y_target;
	}

	friend std::ostream & operator << (std::ostream & out, const Position2D & pos2d)
	{
		out << pos2d.m_x << " " << pos2d.m_y;
		return out;
	}
};

 

Monster.h

Position2D 자체는 재사용을 하려고 만든 클래스이지만, Monster에 속해 있는 Position2D 자체는 다른 클래스에서 사용할 필요가 없다. => 다른 클래스에 속할 수 없음!

Monster 클래스는 멤버로 선언된 Position2D의 기능을 사용하지만, 막상 Position2D 클래스는 Monster와는 상관없이 단순히 '들어온 값'에 의하여 기능을 수행하게 된다. => 단방향!

만약 Position2D 클래스에서도 Monster와 관련된 작업을 해야 한다면, 클래스의 설계가 잘못된 것.

#pragma once
//#include <iostream>
#include <string>
#include "Position2D.h"

class Monster
{
private:
	std::string m_name;	// string: char * data, unsigned length;
	//int m_x;	// location
	//int m_y;
	Position2D m_location;	// sub class => 빨리 만드는 게 좋다
	// 상위 클래스는 호출만 하고, 어떻게 작동하는지는 신경 쓰지 않는 편이 좋음 => 쪼개자!

public:
	//Monster(const std::string name_in, const int & x_in, const int & y_in)
	Monster(const std::string name_in, const Position2D & pos_in)
		: m_name(name_in), m_location(pos_in)
	{}

	//void moveTo(const int & x_target, const int & y_target)
	void moveTo(const Position2D & pos_target)
	{
		//m_x = x_target;
		//m_y = y_target;
		m_location.set(pos_target);
	}

	friend std::ostream & operator << (std::ostream & out, const Monster & monster)
	{
		//out << monster.m_name << " " << monster.m_x << " " << monster.m_y << std::endl;
		out << monster.m_name << " " << monster.m_location;
		return out;
	}
};

 

main_chapter102.cpp

#include "Monster.h"

using namespace std;

int main()
{
	Monster mon1("Sanson", Position2D(0, 0));
	//mon1.m_location;
	cout << mon1 << endl;

	Monster mon2("Parsival", Position2D(0, 0));
	//mon2.m_location;
	// m_location은 mon1의 이름("Sanson")에 대해 알 필요가 없음
	// m_location은 m_location의 기능만 하면 됨


	//while (1)	// gama loop
	{
		// event
		//mon1.moveTo(1, 1);
		mon1.moveTo(Position2D(1,1));
		cout << mon1 << endl;
	}

	return 0;
}

 

 

집합 관계

Aggregation

  • Has-a
  • 사람이 자동차를 가지고 있다.
  • 전체/부품 - 사람/자동차
  • 다른 클래에스도 속할 수 있음 - 자동차는 공유할 수 있고, 팔 수도 있음
  • 멤버의 존재를 클래스가 관리하지 않음 - 사람이 없어도 자동차 클래스는 존재 가능
  • 단방향
더보기

Teacher.h

#pragma once
#include <string>

class Teacher
{
private:
	std::string m_name;
	//TODO: more members like home address, salary, age, evaluation, etc.

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.h

#pragma once
#include <iostream>
#include <string>

class Student
{
private:
	std::string m_name;
	int m_intel;	// intelligence;

	//TODO: add more members like address, phone, favorite food, habits, ...

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

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

};

 

Lecture.h

#pragma once

#include <vector>
#include "Student.h"
#include "Teacher.h"

class Lecture
{
private:
	std::string m_name;

	Teacher teacher;
	std::vector<Student> students;

	//Teacher *teacher;
	//std::vector<Student *> students;

public:
	Lecture(const std::string & name_in)
		: m_name(name_in)
	{}

	~Lecture()
	{
		// do NOT delete teacher
		// do NOT delete students
	}

	void assignTeacher(const Teacher & const teacher_input)
	{
		teacher = teacher_input;
	}

	/*
	void assignTeacher(Teacher * const teacher_input)
	{
		teacher = teacher_input;
	}
	*/

	void registerStudent(const Student & const student_input)
	{
		students.push_back(student_input);
	}

	/*
	void registerStudent(Student * const student_input)
	{
		students.push_back(student_input);
	}
	*/

	void study()
	{
		std::cout << m_name << " Study " << std::endl << std::endl;

		for (auto & element : students)	//Note: 'auto element' doesn't work
			element.setIntel(element.getIntel() + 1);

		/*for (auto element : students)
			(*element).setIntel((*element).getIntel() + 1);*/
	}

	friend std::ostream & operator << (std::ostream & out, const Lecture & lecture)
	{
		out << "Lecture name : " << lecture.m_name << std::endl;

		out << lecture.teacher << std::endl;
		for (auto element : lecture.students)
			out << element << std::endl;

		/*out << *lectrue.teacher << std::endl;
		for (auto element : lecture.students)
			out << *element << std::endl;*/

		return out;
	}
};

 

main_chapter103.cpp

#include <iostream>
#include <vector>
#include <string>
#include "Lecture.h"

int main()
{
	using namespace std;

	// Composition Relationship
	Lecture lec1("Introduction to Computer Programming");
	lec1.assignTeacher(Teacher("Prof. Hong"));
	lec1.registerStudent(Student("Jack Jack", 0));
	lec1.registerStudent(Student("Dash", 1));
	lec1.registerStudent(Student("Violet", 2));

	Lecture lec2("Computational Thinking");
	lec2.assignTeacher(Teacher("Prof. Good"));
	lec2.registerStudent(Student("Jack Jack", 0));

	//TODO: implement Aggregation Relationship

	// test
	{
		cout << lec1 << endl;
		cout << lec2 << endl;

		// event
		lec2.study();

		cout << lec1 << endl;
		cout << lec2 << endl;
	}

	return 0;
}

 

Teacher.h

#pragma once
#include <string>

class Teacher
{
private:
	std::string m_name;
	//TODO: more members like home address, salary, age, evaluation, etc.

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.h

#pragma once
#include <iostream>
#include <string>

class Student
{
private:
	std::string m_name;
	int m_intel;	// intelligence;

	//TODO: add more members like address, phone, favorite food, habits, ...

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

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

};

 

Lecture.h

#pragma once

#include <vector>
#include "Student.h"
#include "Teacher.h"

class Lecture
{
private:
	std::string m_name;

	// value의 vector이기 때문에 push_back을 하면 복사함
	// &student_input != &students[0]
	//Teacher teacher;
	//std::vector<Student> students;

	// students 자체는 vector라서 Lecture가 사라지면 students도 사라짐
	// 단, Student 포인터를 담고 있기 때문에 가리키고 있던 Student 객체들은 사라지지 않음
	Teacher *teacher;
	std::vector<Student *> students;

public:
	Lecture(const std::string & name_in)
		: m_name(name_in)
	{}

	~Lecture()
	{
		// do NOT delete teacher
		// do NOT delete students
	}

	/*
	void assignTeacher(const Teacher & const teacher_input)
	{
		teacher = teacher_input;
	}
	*/

	void assignTeacher(Teacher * const teacher_input)
	{
		teacher = teacher_input;
	}
	
	/*
	void registerStudent(const Student & const student_input)
	{
		students.push_back(student_input);
	}
	*/

	
	void registerStudent(Student * const student_input)
	{
		students.push_back(student_input);
	}
	

	void study()
	{
		std::cout << m_name << " Study " << std::endl << std::endl;

		//for (auto & element : students)	//Note: 'auto element' doesn't work
		//	element.setIntel(element.getIntel() + 1);

		//for (auto & element : students)	//Note: 'auto element' works
		for (auto element : students)
			element->setIntel(element->getIntel() + 1);
	}

	friend std::ostream & operator << (std::ostream & out, const Lecture & lecture)
	{
		out << "Lecture name : " << lecture.m_name << std::endl;

		/*out << lecture.teacher << std::endl;
		for (auto element : lecture.students)
			out << element << std::endl;*/

		out << *lecture.teacher << std::endl;
		for (auto element : lecture.students)
			out << *element << std::endl;

		return out;
	}
};

 

main_chapter103.cpp

#include <iostream>
#include <vector>
#include <string>
#include "Lecture.h"

int main()
{
	using namespace std;

	// Composition Relationship
	Lecture lec1("Introduction to Computer Programming");
	//lec1.assignTeacher(Teacher("Prof. Hong"));
	//lec1.registerStudent(Student("Jack Jack", 0));
	//lec1.registerStudent(Student("Dash", 1));
	//lec1.registerStudent(Student("Violet", 2));

	Lecture lec2("Computational Thinking");
	//lec2.assignTeacher(Teacher("Prof. Good"));
	//lec2.registerStudent(Student("Jack Jack", 0));

	
	// Aggregation Relationship

	// Composition 코드의 문제점:
	// Student(Jack Jack,0)끼리 서로 다른 객체(다른 주소를 가짐)
	Student std1("Jack Jack", 0);
	Student std2("Dash", 1);
	Student std3("Violet", 2);

	Teacher teacher1("Prof. Hong");
	Teacher teacher2("Prof. Good");

	lec1.assignTeacher(&teacher1);
	lec1.registerStudent(&std1);
	lec1.registerStudent(&std2);
	lec1.registerStudent(&std3);

	lec2.assignTeacher(&teacher2);
	lec2.registerStudent(&std1);

	// 1. 메인 함수에서 변수로 선언(상단 구현)
	// 2. 다른 함수에서 사용할 때는 동적 할당으로 구현, 똑같이 사용 가능
	Student *std11 = new Student("Jack Jack", 0);
	Student *std22 = new Student("Dash", 1);
	Student *std33 = new Student("Violet", 2);

	Teacher *teacher11 = new Teacher("Prof. Hong");
	Teacher *teacher22 = new Teacher("Prof. Good");


	// test
	{
		cout << lec1 << endl;
		cout << lec2 << endl;

		// event
		lec2.study();

		cout << lec1 << endl;
		cout << lec2 << endl;
	}

	//TODO: class HobbyClub

	//TODO: delete memory (if necessary)
	delete std11;
	delete std22;
	delete std33;
	delete teacher11;
	delete teacher22;

	return 0;
}

 

분산 처리할 때는 메모리가 분리되어 있기 때문에 불가능! 한 쪽에서 업데이트하면, 다른 쪽에서도 업데이트해야 하기 때문에 동기화 과정을 거쳐야 함!

 

 

제휴(연계) 관계

Association

  • Uses-a
  • 환자는 의사의 치료를 받는다. 의사는 환자들로부터 치료비를 받는다.
  • 서로가 서로를 사용하는 관계
  • 용도 외엔 관계 없음
  • 다른 클래스에도 속할 수 있음
  • 멤버의 존재를 클래스가 관리하지 않음, 서로가 서로를 관리할 수 없음
  • 단방향 or 양방향(을 더 많이 사용)
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Doctor;	// 전방 선언 forward declaration
// 파일 분리 시, 전방 선언한 class가 어디 있는지 찾을 수 없는 경우가 발생할 수 있음!!

class Patient
{
private:
	string m_name;
	vector<Doctor*> m_doctors;	// error! doctor undeclared identifier

public:
	Patient(string name_in)
		: m_name(name_in)
	{}

	void addDoctor(Doctor * new_doctor)
	{
		m_doctors.push_back(new_doctor);
	}

	void meetDoctors();
	/*
	// 전방 선언 시 error! 전방 선언 안에 m_name이 있는지, 없는지를 알 수 없음
	// 따라서 body를 Doctor 클래스의 하단으로 빼 줌
	void meetDoctors()
	{
		for (auto & ele : m_doctors)
		{
			cout <<  m_name << " - Meet doctor : " << ele->m_name << endl;
		}
	}*/

	friend class Doctor;
};

class Doctor
{
private:
	string m_name;
	// 포인터를 쓰지 않고, 환자 고유번호(id)를 부여하여 해당 환자를 찾는 방식으로도 구현 가능
	vector<Patient*> m_patients;
	Doctor * doctor;	// reflexive association

public:
	Doctor(string name_in)
		: m_name(name_in)
	{}

	void addPatient(Patient * new_patient)
	{
		m_patients.push_back(new_patient);
	}

	void meetPatient()
	{
		for (auto & ele : m_patients)
		{
			cout << m_name << " - Meet patient : " << ele->m_name << endl;
		}
	}

	// friend class 선언으로 직접 접근할 수 있게 함
	friend class Patient;
};

void Patient::meetDoctors()
{
	for (auto & ele : m_doctors)
	{
		// 전방 선언 시 error! 전방 선언 안에 m_name이 있는지, 없는지를 알 수 없음
		cout << m_name << " - Meet doctor : " << ele->m_name << endl;
	}
}

int main()
{
	// 어느 한쪽이 확실히 주가 되지 않는 경우(주-주, 부-부)

	Patient *p1 = new Patient("Jack Jack");
	Patient *p2 = new Patient("Dash");
	Patient *p3 = new Patient("Violet");

	Doctor *d1 = new Doctor("Doctor K");
	Doctor *d2 = new Doctor("Doctor L");

	p1->addDoctor(d1);
	d1->addPatient(p1);

	p2->addDoctor(d2);
	d2->addPatient(p2);

	p2->addDoctor(d1);
	d1->addPatient(p2);

	// patients meet doctors
	p1->meetDoctors();

	// doctors meet patients
	d1->meetPatient();

	// deletes
	delete p1;
	delete p2;
	delete p3;

	delete d1;
	delete d2;

	return 0;
}

 

 

의존 관계

Dependency

  • Depends-on
  • 나는 목발을 짚었다.
  • 용도 외엔 관계 없음, 헤더파일에서 사라져도 무관, 대부분의 경우 일시적으로 사용
  • 다른 클래스에도 속할 수 있음
  • 멤버의 존재를 클래스가 관리함, 엄연히 따지자면 멤버로 존재하는 건 아니고 그 '존재'를 클래스가 관리한다는 뜻, 멤버로 들어가지도 않는 경우가 많다
  • 단방향
  • 가장 많이 사용할 것임

Timer.h

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>


class Timer
{
	using clock_t = std::chrono::high_resolution_clock;
	using second_t = std::chrono::duration<double, std::ratio<1>>;

	std::chrono::time_point<clock_t> start_time = clock_t::now();

public:
	void elapsed()
	{
		std::chrono::time_point<clock_t> end_time = clock_t::now();
		std::cout << std::chrono::duration_cast<second_t>(end_time - start_time).count() << std::endl;
	}
};

 

Worker.h

#pragma once

class Worker
{
public:
	void doSomething();
};

 

Worker.cpp

#pragma once
#include "Worker.h"
#include "Timer.h"

// Worker를 선언할 때는 Timer.h가 필요 없고,
// cpp 파일에서 구현할 때면 include 해 주면 됨

void Worker::doSomething()
{
	Timer timer;	// start timer

	// do some work here

	timer.elapsed();	// end timer and report elapsed time
}

 

main.cpp

#include "Worker.h"

int main()
{
	Worker().doSomething();

	return 0;
}

 

 

컨테이너 클래스

다른 클래스들을 담는 역할을 하는 클래스

#include <iostream>
#include <vector>

using namespace std;

class IntArray
{
private:
	int m_length = 0;
	int *m_data  = nullptr;

public:
	//Constructors
	//Destructors
	//Initialize()
	//reset();	// 메모리 싹 지우기
	//resize();
	//insertBefore(const int & value, const int & ix);
	//remove(const int & ix);
	//push_back(const int& value);
	//output operator overloading
};

int main()
{
	IntArray my_arr{ 1, 3, 5, 7, 9 };
	my_arr.insertBefore(10, 1);		// 1, 10, 3, 5, 7, 9
	my_arr.remove(3);				// 1, 10, 3, 7, 9
	my_arr.push_back(13);			// 1, 10, 3, 7, 9, 13

	//standard template library: member of 관계
	//vector<int> int_vec;
	//array<int, 10> int_arr;

	return 0;
}
더보기

매개변수를 어떻게 받아야 할지도 모르겠고 너무 더럽게 짠 것 같다... 시간 남으면 수정해 봐야겠다

#include <iostream>
#include <vector>

using namespace std;

class IntArray
{
private:
	int m_length = 0;
	int *m_data  = nullptr;

public:

	//TODO: 매개변수를 어떻게 받아야 할지 잘 모르겠다..
	IntArray(int n1, int n2, int n3, int n4, int n5)
	{
		m_length = 5;
		m_data = new int[m_length];

		m_data[0] = n1;
		m_data[1] = n2;
		m_data[2] = n3;
		m_data[3] = n4;
		m_data[4] = n5;
	}

	~IntArray()
	{
		delete[] m_data;
	}

	void initialize(int idx, int value)
	{
		if (m_data != nullptr) return;
		m_data[idx] = value;
	}

	void reset()
	{
		if(m_data != nullptr)
			delete[] m_data;
	}

	void resize()
	{
		int *temp = new int[m_length + 1];
		memcpy(temp, m_data, m_length * sizeof(int));
		reset();
		m_data = temp;
		++m_length;
	}

	// 10, 1
	// 1, 10, 3, 5, 7, 9
	void insertBefore(const int & value, const int & ix)
	{
		resize();
		int *temp = new int[m_length - ix - 1];
		for (int i = 0; i < m_length; i++)
		{
			temp[i] = m_data[i + ix];
		}
		memcpy(&m_data[ix+1], temp, (m_length - ix - 1) * sizeof(int));
		m_data[ix] = value;
	}

	// 3
	// 1, 10, 3, 7, 9
	void remove(const int & ix)
	{
		int *temp = new int[m_length - 1];
		for (int i = ix + 1; i < m_length; i++)
		{
			temp[i - ix - 1] = m_data[i];
		}
		memcpy(&m_data[ix], temp, (m_length - ix - 1) * sizeof(int));
		--m_length;
	}

	void push_back(const int& value)
	{
		resize();
		m_data[m_length-1] = value;
	}

	void printTest()
	{
		for (int i = 0; i < m_length; i++)
			cout << m_data[i] << " ";
		cout << endl;
	}

	friend ostream& operator << (ostream & out, const IntArray & arr)
	{
		for (int i = 0; i < arr.m_length; i++)
			out << arr.m_data[i] << " ";
		out << endl;
		return out;
	}

};

int main()
{
	IntArray my_arr{ 1, 3, 5, 7, 9 };
	cout << my_arr;

	my_arr.insertBefore(10, 1);		// 1, 10, 3, 5, 7, 9
	cout << my_arr;
	
	my_arr.remove(3);				// 1, 10, 3, 7, 9
	cout << my_arr;

	my_arr.push_back(13);			// 1, 10, 3, 7, 9, 13
	cout << my_arr;

	my_arr.push_back(15);
	cout << my_arr;

	my_arr.push_back(17);
	cout << my_arr;

	my_arr.remove(1);
	cout << my_arr;

	my_arr.remove(0);
	cout << my_arr;

	//standard template library: member of 관계
	//vector<int> int_vec;
	//array<int, 10> int_arr;

	return 0;
}

 

 

해당 포스트는 '홍정모의 따라하며 배우는 C++' 강의를 수강하며 개인 백업용으로 메모하였습니다.

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

 

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

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

www.inflearn.com

반응형
복사했습니다!