본문 바로가기

개발 공부/C++

따라하며 배우는 C++ 10. 객체 사이의 관계들에 대해

따라하며 배우는 C++ 

10. 객체 사이의 관계들에 대해

 

10.1 객체들의 관계

 

구성(요소) : Composition, Part-of -- 관계와 객체가 분리될 수 없음. 

집합 : Aggregation, Has-a -- 연결이 느슨함. 관계와 객체가 분리될 수 있음.

연계, 제휴: Association, Uses-a -- 단순히 사용하는 관계

의존 : Dependency, Depends-on

 

 

 

10.2 구성 관계 Composition

 

Monter.h

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

class Monster
{
private:
	std::string m_name; //string = char * data, unsigned int length
	//location
	Position2D m_location;

public:
	Monster(const std::string name_in, const Position2D & pos_in)
		:m_name(name_in), m_location(pos_in)
	{

	}

	void moveTo(const Position2D& pos_target)
	{
		m_location.set(pos_target);
	}

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

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: ovaerload operator = 

	void set(const Position2D & pos_target)
	{
		set(pos_target.m_x, 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;
	}
};

 

Source.cpp

#include <iostream>
#include "Monster.h"
using namespace std;

int main() 
{ 
	Monster mon1("Sanson", Position2D(0, 0));
	cout << mon1 << endl;
	{
		mon1.moveTo(Position2D(1, 1));
		cout << mon1 << endl;
	}
	return 0;
}

 

 

이처럼 Monster가 Position을 사용하는 관계를 Composition이라고 한다.

Position이 Monster의 멤버이지만 나중에 다른 클래스에서 재활용할 수도 있는 관계.

 

10.3 집합 관계 Aggregation

 

#include <iostream>
#include <vector>
#include <string>
#include "Lecture.h"
using namespace std;

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");
	lec1.assignTeacher(Teacher("Prof. Good"));
	lec1.registerStudent(Student("Jack Jack", 0));
	lec1.registerStudent(Student("Dash", 1));
	lec1.registerStudent(Student("Violet", 2));

	//TODO : implement Aggregation Relationship

	//tset
	{
		cout << lec1 << endl;
		cout << lec2 << endl;
		//event
		lec2.study();
		cout << lec1 << endl;
		cout << lec2 << endl;
	}
	return 0;
}

Composition으로 일반적으로 구현하면 동일 학생이라도 Lecture에 따라 다른 개체로 인식된다.

 

 

Lecture.h

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

class Lecture
{
private:
	std::string m_name;

	//Composition : 객체와 멤버 변수가 운명을 같이 한다.
	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 << *lecture.teacher << std::endl;
		for(auto element : lecture.students)
			out << *element << std::endl;
		
		*/

		return out;
	}

};

Student.h

#pragma once

#include <iostream>
#include <string>

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

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

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

	const std::string& getName()
	{
		return m_name;
	}

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

위와 같이 만들면, 같은 학생일지라도 제대로 인식이 되지 않는다.

 

 

source.cpp

 

#include <iostream>
#include <vector>
#include <string>
#include "Lecture.h"
using namespace std;

int main() 
{ 
	using namespace std;
	Student std1("Jack Jack", 0);
	Student std2("Dash", 1);
	Student std3("Violet", 2);

	Teacher teacher1("Prof. Hong");
	Teacher teacher2("Prof. Good");
	//Composition Relationship
	Lecture lec1("Introduction to Computer Programming");
	lec1.assignTeacher(&teacher1);
	lec1.registerStudent(&std1);
	lec1.registerStudent(&std2);
	lec1.registerStudent(&std3);

	Lecture lec2("Computational Thinking");
	lec2.assignTeacher(&teacher2);
	lec2.registerStudent(&std1);

	//TODO : implement Aggregation Relationship

	//tset
	{
		cout << lec1 << endl;
		cout << lec2 << endl;
		//event
		lec2.study();
		cout << lec1 << endl;
		cout << lec2 << endl;
	}
	return 0;
}

Lecture.h

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

class Lecture
{
private:
	std::string m_name;

	//Composition : 객체와 멤버 변수가 운명을 같이 한다.
	/*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 << *lecture.teacher << std::endl;
		for(auto element : lecture.students)
			out << *element << std::endl;
		
		

		return out;
	}

};

 

Student.h

 

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

class Lecture
{
private:
	std::string m_name;

	//Composition : 객체와 멤버 변수가 운명을 같이 한다.
	/*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 << *lecture.teacher << std::endl;
		for(auto element : lecture.students)
			out << *element << std::endl;
		
		

		return out;
	}

};

 

Teacher.h

 

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

class Lecture
{
private:
	std::string m_name;

	//Composition : 객체와 멤버 변수가 운명을 같이 한다.
	/*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 << *lecture.teacher << std::endl;
		for(auto element : lecture.students)
			out << *element << std::endl;
		
		

		return out;
	}

};

source.cpp에서 선언된 인스턴스를 다른 곳에서도 써야 할 때는 Student *std1 = new Student(~~) 처럼 동적으로 구현하기도 한다. 대신에 꼭 delete는 해 주어야 한다.

 

위의 경우는 lecture 두 개의 객체가 한 개의 student들을 공유하고 있는 형태이다.

그러나 분산 처리에선 아예 다른 컴퓨터들이 작동하기 때문에 "사본"을 가질 수 밖에 없다.

그러므로 정보 업데이트를 위한 싱크로나이즈, 동기화 작업이 필요하다.

 

 

10.4 제휴 관계 Association

 

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Doctor; //forward declaration
//전방 선언일 경우 Association관계일 가능성이 높다.

class Patient
{
private:
	string m_name;
	vector<Doctor*> m_doctors;
public:
	Patient(string name_in)
		: m_name(name_in)
	{ }

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

	void meetDoctors();

	friend class Doctor;
};

class Doctor
{
private:
	string m_name;
	vector<Patient*> m_patients;
public:
	Doctor(string name_in)
		: m_name(name_in)
	{ }

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

	friend class Patient; //멤버 직접 접속 가능.
	void meetPatients()
	{
		for (auto& ele : m_patients)
		{
			cout << "Meet patients : " << ele->m_name << endl;
		}
	}
};

void Patient::meetDoctors()
{
	for (auto& ele : m_doctors)
	{
		cout << "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->meetPatients();

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

	delete d1;
	delete d2;
	return 0;
}

 

Reflecsive 제휴관계 : class 안에 같은 class를 참조하는 것!

 

 

10.5 의존 관계 Dependency

 

source.cpp

#include "Worker.h"
using namespace std;


int main() 
{ 
	Worker().doSomething();
	return 0;
}

Worker.h

#include "Worker.h"
using namespace std;


int main() 
{ 
	Worker().doSomething();
	return 0;
}

Worker.cpp

#include "Worker.h"
#include "Timer.h"

#pragma once

void Worker::doSomething()
{
	Timer timer;
	//do some work here
	timer.elapsed();
}

 

Timer.h

#pragma once
#include <vector>
#include <string>
#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>
using namespace std;
class Timer
{
	using clock_t = chrono::high_resolution_clock;
	using second_t = chrono::duration<double, ratio<1>>;
	chrono::time_point<clock_t> start_time = clock_t::now();
public:
	void elapsed()
	{
		chrono::time_point<clock_t> end_time = clock_t::now();
		cout << chrono::duration_cast<second_t>(end_time - start_time).count() << endl;
	}
};

 

10.6 컨테이너 클래스

 

vector나 array가 container. 

Standard Template Library > Containers

ㅇㅇ가 ㅇㅇ의 멤버다라는 관계를 표시하기 좋다.

 

#include <iostream>
using namespace std;

/* Assignment */
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);

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