본문 바로가기
Language/C++

[C++_제 9장] 객체와 클래스

by 전전긍긍 2022. 3. 10.

본 포스팅은 C++로 시작하는 객체지향 프로그래밍 책을 바탕으로 작성하였습니다.

 

C++로 시작하는 객체지향 프로그래밍

『C++로 시작하는 객체지향 프로그래밍』은 구문보다는 문제 해결에 중점을 두는 문제 구동 방식을 사용한 프로그래밍에 대해 가르치고 있다. 여러 가지 상황에서 문제를 야기한 개념을 사용함

book.naver.com


⭐key point

클래스는 객체의 속성과 행동을 정의한다.

 

객체의 상태(state)는 현재 값을 가지고 있는 데이터 필드로 표현된다. 객체의 행동(behavior)함수에 의해 정의된다. 객체에 대한 함수를 호출하는 것은 객체에 어떤 동작을 수행하도록 요구하는 것.

 

클래스는 객체에 대한 정의이며, 객체는 클래스로부터 생성된다.

객체를 생성할려면 클래스에 객체가 사용하려는 상태와 행동이 정의되어 있어야 하기 때문이다.

 

생성자(constuctor)객체를 생성하기 위해 호출된다.

 

- 생성자는 클래스 자신과 같은 이름을 가져야 한다.

- 생성자는 반환 유형이 없다. (void도 사용할 수 없음)

- 생성자는 객체가 생성될 때 호출된다. 생성자는 객체 초기화의 역할을 수행한다. (데이터 필드 초기화)

 

* 클래스 멤버는 데이터 필드가 선언될 때 초기화 할 수 없다. 그래서 생성자를 이용하여 초기화 한다.

* 인수X, 내용X 생성자 → 기본 생성자(defalut constuctor)

* 생성자 초기화 목록(initializer list)


- 객체의 데이터와 함수는 객체 이름과 점 연산자(.)를 사용하여 접근할 수 있다.

 

- 인수 없는 생성자를 사용하여 객체 생성

ClassName objectName;

- 인수 있는 생성자를 사용하여 객체 생성

ClassName objectName(args);

objectName.dataField는 객체의 데이터 필드를 참조한다. 인스턴스 멤버 변수는 특정한 인스턴스에 종속적이다.

objectName.function(인수)은 객체에 대한 함수를 호출한다. 특정한 인스턴스에 대해서만 함수가 호출되는데, 인스턴스 함수를 호출하는 객체를 호출 객체(calling object)라고 부른다.

 

* 객체 이름이 선언되면 재할당 불가. 

* 객체를 생성하고 한 번만 사용할 것이라면, 이름을 지어줄 필요가 없기 때문에 익명 함수라고 한다.


클래스 정의와 클래스 구현분리함으로써 클래스의 유지보수를 손쉽게 할 수 있다.

 

클래스 정의(definition) : 클래스의 규약 사항을 기술. 모든 데이터 필드와 생성자 원형(prototype), 함수 원형을 목록으로 만듦. * 확장자 : .h

클래스 구현(implementation) : 정의부분에서 기술한 규약을 실현하는 것. 생성자와 함수를 만듦. * 확장자 : .cpp

:: 이항 범위 지정 연산자 -> 클래스에서 클래스 멤버의 범위를 나타내 준다.

 

다중 포함 방지 : 헤더 파일이 여러 번 포함되는 것을 방지하기 위해서는 포함 감시(inclusion guard)를 사용한다.

 

#ifndef : '만일 정의되어 있지 않으면(if not defined)' 정의되어 있는지 검사

#define : 만일 정의되어 있지 않다면 정의하고, 헤더파일의 나머지 부분을 포함시킨다.

(만일 정의되어 있다면, 헤더 파일의 나머지 부분은 건너뛴다)

#endif : 헤더 파일의 끝을 나타내기 위해 필요하다.


데이터 필드 캡슐화 : 데이터 필드전용(private)으로 만들면 데이터를 보호할 수 있고 클래스의 유지보수가 쉽다.

public으로 하면 데이터가 임의로 수정될 수도 있고, 유지보수를 어렵게 만들기도 하며 버그가 생기기 쉽기 때문에 좋은 방식은 아니다. 속성에 대한 직접적인 수정을 피하기 위해 private 키워드를 사용하여 데이터 필드를 전용(private)으로 선언해야 한다. 이를 데이터 필드 캡슐화(data field encapsulation)이라고 한다.

 

전용 데이터 필드(private data field)에 접근하기 위해서는 get함수와 set 함수를 사용한다.

get 함수 : 데이터 필드의 값을 반환

set 함수 : 전용 데이터 필드를 새로운 값으로 변경

public:
	double getRadius();
	void setRadius(double);

private:
	double radius;

변수의 범위

인스턴스와 정적 변수의 범위는 변수가 선언된 위치에 상관없이 클래스 전체가 된다.

- 데이터 필드 변수 : 단 한 번만 사용 가능. 함수 안에서 선언한 변수 이름은 다른 함수에서 사용 가능

- 지역 변수 : 함수 안에서만 선언하고 그 지역 안에서만 사용가능. 지역변수에서 데이터 필드와 이름이 같다면, 지역 변수가 우선권을 가지고, 같은 이름의 데이터 필드는 숨겨진다.

 

밑에 예제 코드에서 x는 10으로 데이터 필드 변수가 선언하였는데 p라는 함수에서는 지역변수가 우선권을 받기 때문에 20이 출력된다. y는 데이터 필드로 선언되었고, 함수 p() 내부에서 접근이 가능하다.

#include <iostream>
using namespace std;

class Foo
{
public:
	int x; //데이터 필드
	int y; //데이터 필드

	Foo()
	{
		x = 10;
		y = 10;
	}

	void p()
	{
		int x = 20; //지역함수
		cout << "x is  " << x << endl; //20
		cout << "y is  " << y << endl; //10
	}
};

int main()
{
	Foo foo;
	foo.p();

	return 0;
}

클래스 추상화와 캡슐화

클래스 추상화(class abstraction) : 클래스 구현과 클래스의 사용을 분리하는 것.

클래스 캡슐화(class encapsulation) : 클래스 구현 세부 사항은 캡슐화되고 사용자에게 감춰진 상태로 되는 것.


list (더보기에서 확인)

더보기

9.2 (클래스 정의와 객체 생성의 예) TV의 객체를 생성하고 각 객체에 따른 속성과 함수 정의

#include <iostream>
using namespace std;

//class
class TV
{
public:
	int channel;
	int volumeLevel;
	bool on;

	//constructor
	TV()
	{
		channel = 1;
		volumeLevel = 1;
		on = false;
	}

	//function
	void turnOn()
	{
		on = true;
	}

	void turnOff()
	{
		on = false;
	}

	void setChannel(int newChannel)
	{
		if (on && newChannel >= 1 && newChannel <= 120)
			channel = newChannel;
	}

	void setVolume(int newVolumeLevel)
	{
		if (on && newVolumeLevel >= 1 && newVolumeLevel <= 7)
			volumeLevel = newVolumeLevel;
	}

	void channelUp()
	{
		if (on && channel < 120)
			channel++;
	}

	void channelDown()
	{
		if (on && channel > 1)
			channel--;
	}

	void volumeUp()
	{
		if (on && volumeLevel < 7)
			volumeLevel++;
	}

	void volumeDown()
	{
		if (on && volumeLevel > 1)
			volumeLevel--;
	}
};

//main
int main()
{
	TV tv1;
	tv1.turnOn();
	tv1.setChannel(30);
	tv1.setVolume(3);

	TV tv2;
	tv2.turnOn();
	tv2.channelUp();
	tv2.channelUp();
	tv2.setVolume(5);
	tv2.volumeDown();

	cout << "tv1's channel is " << tv1.channel << " and volume level is " << tv1.volumeLevel << endl; //30 3
	cout << "tv2's channel is " << tv2.channel << " and volume level is " << tv2.volumeLevel << endl; //3 4

	return 0;
}

//tv1's channel is 30 and volume level is 3
//tv2's channel is 3 and volume level is 4

 

9.9 데이터 필드 캡슐화

(header 정의)

#ifndef Circle_H
#define Circle_H

class Circle
{
public:
	Circle();
	Circle(double);
	double getArea();
	double getRadius();
	void setRadius(double);

private:
	double radius;
};

#endif // !Circle_H

(구현)

#include "CircleWithPrivateDataFields.h"

Circle::Circle()
{
	radius = 1;
}

Circle::Circle(double newRadius)
{
	radius = newRadius;
}

double Circle::getArea()
{
	return radius * radius * 3.14159;
}

double Circle::getRadius()
{
	return radius;
}

void Circle::setRadius(double newRadius)
{
	radius = (newRadius >= 0) ? newRadius : 0;
}

(main)

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

int main()
{
	Circle circle1;
	Circle circle2(5.0);

	cout << "The area of the circle of radius " << circle1.getRadius() << " is " << circle1.getArea() << endl;
	cout << "The area of the circle of radius " << circle2.getRadius() << " is " << circle2.getArea() << endl;

	circle2.setRadius(100);
	cout << "The area of the circle of radius " << circle2.getRadius() << " is " << circle2.getArea() << endl;

	return 0;
}
/*
The area of the circle of radius 1 is 3.14159
The area of the circle of radius 5 is 78.5397
The area of the circle of radius 100 is 31415.9
*/

프로그래밍 실습 (더보기에서 확인)

9.1 (Rectangle 클래스) 직사각형을 나타내는 Rectangle이란 이름의 클래스를 설계하여라. 클래스는 다음을 포함한다.

- 직사각형의 폭과 높이를 나타내는 width와 hieght라는 이름의 double 데이터 필드

- width와 height의 값이 1인 직사각형을 생성하는 인수 없는 생성자

- 지정된 width와 height를 갖는 기본 직사각형을 생성하는 생성자

- 모든 데이터 필드에 대한 접근자와 변경자 함수

- 직사각형의 면접 값을 반환하는 getArea() 함수

- 둘레 길이를 반환하는 getPerimeter() 함수

더보기

📍Rectangle.h

#ifndef RECTANGLE_H
#define RECTANGLE_H

class Rectangle {
private:
	double weight; //폭
	double height; //높이
public:
	Rectangle();
	Rectangle(double, double);
	double getArea();
	double getPerimeter();
	double getHeight();
	void setHeight(double);
	double getWeight();
	void setWeight(double);
};

#endif // !RECTANGLE_H

📍Rectangle.cpp

#include "Rectangle.h"

Rectangle::Rectangle()
{
	weight = 1;
	height = 1;
}

Rectangle::Rectangle(double newWeight, double newHeight)
{
	weight = newWeight;
	height = newHeight;
}

double Rectangle::getArea()
{
	return weight * height;
}

double Rectangle::getPerimeter()
{
	return 2 * (weight + height);
}

double Rectangle::getHeight()
{
	return height;
}

void Rectangle::setHeight(double newHeight)
{
	height = (newHeight >= 0) ? newHeight : 0;
}

double Rectangle::getWeight()
{
	return weight;
}

void Rectangle::setWeight(double newWeight)
{
	weight = (newWeight >= 0) ? newWeight : 0;
}

📍main.cpp

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

int main() {
	Rectangle rectangle1(4, 40);
	Rectangle rectangle2(3.5, 35.9);
	Rectangle rectangle3;

	cout << "rectangle1 weight = " << rectangle1.getWeight() << " height = "
		<< rectangle1.getHeight() << endl;
	cout << "rectangle1 Area = " << rectangle1.getArea() << " perimeter = "
		<< rectangle1.getPerimeter() << endl;

	cout << endl; 

	cout << "rectangle2 weight = " << rectangle2.getWeight() << " height = "
		<< rectangle2.getHeight() << endl;
	cout << "rectangle2 Area = " << rectangle2.getArea() << " perimeter = "
		<< rectangle2.getPerimeter() << endl;

	cout << endl;

	cout << "rectangle3 weight = " << rectangle3.getWeight() << " height = "
		<< rectangle3.getHeight() << endl;
	cout << "rectangle3 Area = " << rectangle3.getArea() << " perimeter = "
		<< rectangle3.getPerimeter() << endl;
}

 

'Language > C++' 카테고리의 다른 글

[C++_제 10장] 객체지향 개념  (0) 2022.05.07
[C++_백준] 10828, 10845  (0) 2022.03.16
[C++_백준] 16430, 15740, 15964  (0) 2022.02.12
[C++_백준] while문 (10952, 10951, 1110)  (0) 2022.02.08
[C++_백준] 16394, 15894  (0) 2022.02.04