IT 프로그래밍/C++

[C++]클래스와 객체

기술1 2024. 4. 6. 12:11
반응형

클래스의 등장 배경 

전화번호부 프로그램을 만든다고 가정하면 한 사람의 이름과 전화번호는 항상 같이 붙어다녀야 하는 데이터입니다. 이 두 가지 데이터를 서로 별개의 변수에 저장하면 우리가 이름 데이터를 옮길 때마다 전화번호 데이터도 따로 옮겨줘야 합니다.

 

만약 각 사람에 대한 이름과 전화번호 뿐만 아니라 주소, 이메일 등 여러가지 데이터를 저장한다면 이 불편함은 더 심해질 것입니다. 인덱스 메이커 프로그램에서도 하나의 단어와 그 단어가 등장한 라인 번호들은 항상 같이 붙어다녀야 하는 데이터입니다. 

 

이런 식으로 서로 관련있는 데이터를 하나의 단위로 묶는 것은 편할 것입니다. 이것이 클래스라는 개념이 등장하는 가장 기본적인 이유입니다. 

 

 

예시코드

class SalesData
{
public:
	string isbn;
	int units_sold = 0;
	double revenue = 0.0;
};

이 클래스의 주 목적은 하나의 책의 ISBN번호, 판매된 권수, 그리고 총 매출을 하나의 단위로 묶는 것입니다. 여리서 isbn, units_sold, revenue 등을 클래스 SalesData의 필드(filed), 데이터 필드, 데이터 멤버 등으로 부릅니다. 즉 클래스 SalesData는 3개의 필드를 가지고 있습니다. 

 

멤버의 값을 이렇게 class의 정의 내부에서 초기화할 수 있습니다. C의 STRUCT와 class는 같은 개념인데 c++역시 struct를 제공하지만 c++의 struct는 class와 유사하며 차이는 미세한 편입니다. 

int main()
{
	SalesData first;
	first.isbn = "978-0-321-71411-4";
	first.units_sold = 3;
	first.revenue = 32000;
	cout << first.isbn << " " << first.units_sold << " " << first.revenue << endl;

	SalesData data[10];
	data[0] = first;
	data[1].isbn = "978-0-111-71524-1";
	data[1].units_sold = 11;
	data[1].revenue = 56000;

	cout << data[1].isbn << " " << data[1].units_sold << " " << data[1].revenue << endl;
	
	return 0;
}

먼저 위에 first는 SalesData 타입의 객체를 생성한 것이며 이를 first라고 한 것입니다. class 내의 변수를 만들어주었다고 보시면 됩니다. first객체의 필드들에 .연산자로 접근하여 값을 쓰고 읽습니다. 

 

그리고 그 밑에 SalesData data[10];은 SalesData타입의 배열을 만든 것인데요. 클래스 객체 간 치환도 아래와 같이 data[0] = first;와 같이 가능합니다. 


first는 하나의 변수이며 그 변수 안에 3개의 칸이 있는 것입니다. data는 배열이며 그 배열 0 1 2 3.. 각각의 방 안에 3개의 칸이 있다고 생각하시면 됩니다. 

출처/ 국립부경대 권오흠 교수님 자료

이런 식으로 first와 data간에도 치환이 가능한 것입니다. 

 

클래스는 결국 하나의 타입이라고 보시면 됩니다. like int, double.. 다만 int double처럼 미리 정해진 타입이 아니라 사용자가 정의한 새로운 타입이라는 의미에서 사용자 정의 타입이라고 부르기도 합니다.

 

int혹은 double형 변수를 선언하고 사용하는 것처럼 SalesData형 변수나 배열 혹은 벡터를 선언하고 사용하는 것입니다. 

 

 

객체(object)간의 치환연산

int type의 변수를 만들어야 실체가 생기는 것이며 int는 하나의 type이라 공간이나 메모리를 차지하는 것은 아닙니다. class도 마찬가지로 하나의 type이기 때문에 그 Classtype의 하나의 변수를 객체 혹은 instance 라고 부릅니다. 

int main()
{
	SalesData  p;
	p.isbn = "978-0-321-71411-4";
	p.units_sold = 3;
	p.revenue = 32000;

	SalesData q;
	q = p;
	q.isbn = "112-0-321-76318-4";

	SalesData arr[10];
	arr[0] = q;

	return 0;
}

한 객체를 동일한 클래스의 다른 객체에 위와 같이 치환할 수 있습니다. 

 

q = p

q의 멤버들은 pp의 멤버들과 동일한 값을 가집니다. 복사한 후에는 따로 수정할 수 있습니다. class 안에 여러가지 데이터가 있는데  이렇게 한번에 넘길 수 있으며 이는 객체를 매개변수와 반환값으로 사용할 수 있다는 의미입니다. 

 

arr[0] = q

arr[0] = q 같이 arr[0]의 멤버들은 q의 멤버들과 동일한 값을 가집니다. 

 

객체를 매개변수와 반환값으로 해주는 것

 

class SalesData
{
public:
	string isbn;
	int units_sold = 0;
	double revenue = 0.0;
};

SalesData combine(SalesData x, SalesData y)
{
	SalesData z;
	z.isbn = x.isbn;
	z.units_sold = x.units_sold + y.units_sold;
	
	z.revenue = x.revenue + y.revenue;

	return z;
}

int main()
{
	SalesData a, b, c;
	a.isbn = "978-0-321-71411-4";
	a.units_sold = 3;
	a.revenue = 32000;

	b.isbn = "978-0-321-71411-4";
	b.units_sold = 2;
	b.revenue = 25000;

	if (a.isbn == b.isbn)
		c = combine(a, b);

	return 0;
}

위 식을 보면 객체를 함수의 매개변수와 반환값으로도 사용을 해주었습니다. 

 

위의 식은 만약 a와 b의 isbn이 같다면 합쳐지는 것인데요. 그러면 해당 함수에 따라 units_sold와 revenue가 합쳐진 값이 하나의 c로 나오게 되는 것입니다. 

 

객체 참조의 return 문제점

 

SalesData &combine(SalesData &x, SalesData &y)
{
	SalesData z;
	z.isbn = x.isbn;
	z.units_sold = x.units_sold + y.units_sold;
	
	z.revenue = x.revenue + y.revenue;

	return z;
}

만약 이렇게 참조를 걸어준다면 어떻게 될까요? combine은 return z를 참조하는 것이 되겠죠? 그리고 매개변수도 참조를 해주었기 때문에 매개변수를 참조해서 가져오면서 객체와의 복사를 피할 수 있으므로 덩치가 클 때는 효율적일 수 있습니다. 

 

하지만 만약 이렇게 할 경우 SalesData combine 함수를 쓰는 것은 int main()이기 떄문에 지역변수인 SalesData z는 해당 main으로 갈 때 소멸되게 됩니다. 객체 z 자체는 소멸됩니다. 

 

이렇게 하면 보통은 이런 부분에 대한 문제를 지적하면서 warning을 안내해줍니다. 

1>C:\dev\study\Project1\미님.cpp(21): warning C4172: 지역 변수 또는 임시: z의 주소를 반환하고 있습니다.

따라서 참조에 의한 반환을 하는 것은 함수가 종료된 이후에도 여전히 z가 존재하도록 만들어주면 됩니다. 예를들면 z가 전역변수면 가능하겠죠? 하지만 그런 경우에는 굳이 전역변수를 반환할 필요가 없습니다. 또 다른 경우는 매개변수를 참조로 받았다면 

반응형