1주차 9장 객체와 클래스 1,2
1. 구조체와 클래스
1. 구조체의 정의와 역할
구조체는 c언어에서부터 존재한 사용자 정의 자료형(User-Defined Types)을 말합니다. 기본 자료형(primitive data type)인 int, float 등이 변수가 선언될 때 앞에 사용되는 것처럼 변수 선언할 때 사용됩니다. 아래의 예제를 보면 CDAccount는 term의 int와 같은 역할을 합니다.
struct CDAccount
{
double balance;
double interestRate;
int term;
};
CDAccount account;
//int term 처럼
이처럼 구조체는 c언어의 기본 타입 여러 개를 묶어서 프로그래머가 정의해 놓는 것으로 다양한 자료의 집합입니다. 단, c언어에서 제공하지 않는 타입을 만들어 사용할 수는 없습니다.
+a) 구조체와 패딩바이트
변수가 선언되면 메모리에 그 자료형을 저장할 공간이 정해집니다. 이때 구조체의 크기는 구조체를 구성하고 있는 변수들의 type 크기에 따라 달라집니다.
#include <cstdio>
struct st1
{
int a;
int b;
char c;
char d;
double e;
short f;
};
struct st2
{
double e;
int a;
int b;
char c;
char d;
short f;
};
int main()
{
printf("구조체 st1의 크기 : %d\n", sizeof(st1));
printf("구조체 st2의 크기 : %d", sizeof(st2));
return 0;
}


2. 구조체의 사용
1) 구조체 마지막에 세미콜론(;)을 잊으면 안됩니다.
2) 구조체는 멤버 변수에 접근하기 위해서 .연산자를 사용합니다.
3) 구조체도 변수 선언과 동시에 초기화 할 수 있습니다. 단, 이후에 배울 생성자로 초기화한 후에는 변수 선언과 동시에 초기화 할 수 없습니다.
struct CDAccount
{
double balance;
double interestRate;
int term;
};
int main()
{
//1번 변수선언과 동시에 초기화
CDAccount myAccount = {1000000, 3.5, 12};
//2번 변수 선언후, .을 이용한 멤버변수 접근
CDAccount yourAccount;
yourAccount.balance = 2000000;
yourAccount.interestRate =3.8;
yourAccount.term =18;
return 0;
}
3. 클래스
클래스는 c언어의 구조체룰 c++에서 더욱 발전시킨 개념입니다. 구조체와 달리 클래스는 멤버변수 뿐 아니라 멤버함수를 가질 수 있습니다. 클래스형 변수를 객체 또는 인스턴스라고 합니다. 또한 클래스는 객체지향 프로그래밍 기법의 가장 중심 개념으로 c언어와 c++을 구분 짓는 가장 큰 특징입니다.
#include <iostream>
#include <iomanip>
//setprecision을 사용하기 위해 필요한 헤더파일
using namespace std;
class CDAccount
{
public:
double balance;
double interestRate;
int term;
void print() {
double result;
result = balance + balance * (interestRate / 100) * term / 12;
cout << fixed << setprecision(0) << result << endl;
// setprecision :출력 범위를 지정합니다.
//fixed가 없는 경우엔 정수부+소수부 기준으로 반올림하여 출력하지만,
//fixed가 있는 경우엔 소수분만을 기준으로 다섯자리를 반올림하여 출력합니다
}
};
int main()
{
CDAccount yourAccount;
yourAccount.balance = 2000000;
yourAccount.interestRate = 3.8;
yourAccount.term = 18;
yourAccount.print();
return 0;
}
2. 객체와 클래스
1. 객체를 위한 클래스 정의
객체 지향 프로그래밍(OOP. Object-Oriented Programming)에서는 프로그래밍에 객체(object)를 사용합니다. 객체의 상태(state)는 데이터 필드, 즉 변수로 표현이되고 이 변수에 데이터가 저장됩니다. 객체의 행동(behavior)은 함수에 의해 정의됩니다. 객체 지향 프로그래밍에서 객체의 멤버(member)는 데이터 필드와 함수를 의미합니다. 클래스는 멤버 변수와 멤버 함수로 이루어져 있습니다.
2. 클래스의 정의와 객체 생성의 예
클래스와 객체는 붕어빵틀과 붕어빵으로 자주 비유됩니다. 클래스(class)는 객체가 어떻게 생겨야 하는지를 적어놓은 설계도와 같습니다. 객체(object)는 설계도를 통해 만들어진 인스턴스(instance)입니다.
리스트 9.1 ▼
#include <iostream>
using namespace std;
class Circle
{//기본 클래스가 소문자로 시작하기 때문에 구분을 위해서
//사용자정의 클래스는 암묵적으로 대문자로 시작합니다.
public:
double radius;
Circle() {
radius = 1;
}
Circle(double newRadius) {
radius = newRadius;
}
double getArea() {
return radius * radius * 3.14159;
}
}; //반드시 세미콜론이 있어야 합니다.
int main() {
Circle c1; //인수가 없는 생성자를 사용할 때에는 괄호가 있으면 안됩니다.
Circle c2(25);
Circle c3(125);
cout << "c1의 반지름 : " << c1.radius << " 면적 : " << c1.getArea() << endl;
cout << "c2의 반지름 : " << c2.radius << " 면적 : " << c2.getArea() << endl;
cout << "c3의 반지름 : " << c3.radius << " 면적 : " << c3.getArea() << endl;
return 0;
}
3. 생성자
생성자는 클래스에 존재하는 특별한 기능을 가진 함수입니다. 생성자는 객체가 생성될 때 호출되기 때문에 객체 초기화 역할을 자주 사용됩니다. 클래스로 찍어낸 변수는 main함수 안에서 따로 초기화할 수 있지만 클래스 멤버로서 데이터 필드는 클래스 선언 안에서 초기화할 수 없습니다. 하지만 생성자는 main함수에서 객체를 생성(선언)할 때 자동으로 가장 먼저 불려오는 함수이기 때문에 객체를 초기화하는 데 이용할 수 있습니다. main함수에서 이용할 파일을 열어 놓는 데 사용하기도 합니다.
생성자에는 여러 가지 특성이 있습니다.
1) 생성자는 클래스 자신과 같은 이름을 가져야 합니다.
2) 생성자는 반환 유형이 없는 함수입니다.
3) 변수를 초기화하는 데 사용합니다.
4) 생성자 함수는 오버로딩이 가능합니다.(c++의 함수 특성)
5) 생성자는 (main함수에서) 호출이 불가능합니다.
6) 생성자는 클래스 변수를 만들때(객체가 생성될 때) 한번 자동으로 호출되기 때문에 접근 지정자가 public: 으로 설정되어야 합니다.
프로그래머가 생성자를 만들지 않으면 c++ 컴파일러가 매개변수, 내용이 비어 있는 기본 생성자(default constructor)을 만듭니다. 하지만 프로그래머가 생성자를 만들면 기본 생성자를 제공하지 않습니다. 그러므로 매개변수가 있는 생성자를 사용하다가 매개변수가 없는 생성자를 사용하고 싶다면 프로그래머가 직접 정의한 후에 사용할 수 있습니다.
인수가 없는 생성자를 호출할 때는 ()괄호를 사용하지 않습니다.
4. 생성자의 초기화 목록
생성자에 초기화 목록(initializer list)를 사용하여 초기화할 수 있습니다. 초기화 목록을 사용하면 좀 더 빠른 속도로 프로그램이 돌아갑니다. 변수를 선언하고 이후, 초기화하는 것보다 변수 선언과 동시에 초기화하는 것이 더 빠른 것과 같습니다. CDAccount{ int a; a=1;} 을 CDAccount{ int a=1;} 로 바꾼 것과 비슷하다고 이해하시면 편합니다.
// 보통
Circle() {
radius = 1;
}
Circle(double newRadius) {
radius = newRadius;
}
// 초기화 목록
Circle(): radius(1) {
}
Circle(double newRadius) : radius(newRadius){
}
5. 객체의 생성과 사용 정리
1) 인수가 없는 생성자로 생성한 객체는 ()괄호를 사용하지 않습니다.
2) 객체가 생성된 후에는 객체 멤버 접근 연산자(object member access operator) . 점 연산자를 사용하여 데이터에 접근하고 함수를 호출합니다.
3) 대입연산자(=)를 사용해 하나의 객체로부터 다른 객체로 데이터필드를 복사할 수 있습니다.
//c1, c2는 다른 객체이지만 radius 값은 같습니다.
Circle c1 =c2;
4) 객체의 크기는 데이터필드의 크기와 같습니다. 멤버함수는 같은 클래스의 모든 객체에 공유되므로 하나의 복사본만 생성됩니다. 그래서 실제로 객체의 크기는 크지 않습니다.
5) 객체를 한번만 사용하고 싶다면 익명개체(anonymous object)를 사용할 수 있습니다.
cout << "Area is "<< Circle().getArea() << endl;
cout << "Area is " << Circle(5).getArea() << endl;
TV.cpp예제
#include <iostream>
using namespace std;
class TV {
public:
int channel;
int volumnLevel;
bool on;
TV() {
channel = 1;
volumnLevel = 1;
on = false;
}
void turnOn() {
on = true;
}
void turnOff() {
on = false;
}
void setChannel(int newChannel) {
if (on && newChannel >=1 && newChannel <=120)
channel = newChannel;
}
void setVolumn(int newVolumnLevel) {
if(on && newVolumnLevel>=1 && newVolumnLevel<=7)
volumnLevel = newVolumnLevel;
}
void channelUp() {
if (on && channel < 120)
channel++;
}
void channelDown() {
if (on && channel >1)
channel--;
}
void volumnUp() {
if (on && volumnLevel < 7)
volumnLevel++;
}
void volumnDown() {
if (on && volumnLevel > 1)
volumnLevel--;
}
};
int main() {
TV tv1;
tv1.turnOn();
tv1.setChannel(30);
tv1.setVolumn(3);
TV tv2;
tv2.turnOn();
tv2.channelUp();
tv2.channelUp();
tv2.volumnUp();
// 생성자 초기화와 동시에 변수 초기화는 같이 할 수 없다.
// TV tv3 = { 35,6,true };
cout << "tv1's channel is " << tv1.channel << " and volumn level is " << tv1.volumnLevel << endl;
cout << "tv2's channel is " << tv2.channel << " and volumn level is " << tv2.volumnLevel << endl;
return 0;
}
6. check point
Q 9.1) 객체와 객체의 클래스의 정의와 관계를 설명하라. 클래스 정의는 어떻게 하는가? 객체를 선언하고 생성하는 방법은 무엇인가?
A) 클래스는 객체를 만들기 위한 설계도입니다. 클래스에서 형식을 미리 정해 놓고 그 형식에 맞춰 객체(인스턴스)를 생성합니다.
객체는 아래와 같이 선언합니다.(==생성)
//인수가 없는 생성자의 경우
ClassName c1;
//인수가 있는 생성자의 경우
ClassName c2(5);
Q 9.2) 생성자와 함수의 차이점은 무엇인가?
A) 생성자는 클래스만의 특별한 함수입니다. 생성자는 return값이 없고 호출할 수 없습니다.
Q 9.3) 인수 없는 생성자를 사용하여 객체를 생성하는 방법은 무엇인가? 인수있는 생성자를 사용하여 객체를 생성하는 방법은 무엇인가?
//인수가 없는 생성자의 경우
ClassName c1;
//인수가 있는 생성자의 경우
ClassName c2(5);
Q 9.4) 객 체 이름이 선언된 수, 다른 객체를 참조하기 위해 객체 이름을 재할당할 수 있는 가?
A) 선언된 객체이름은 배열이름처럼 다시 할당할 수 없습니다. 객체의 내용은 변할 수 있지만 객체의 이름은 바꿀 수 없습니다. 따라서 한번 사용하는 객체의 경우 익명객체(annonymous object)를 사용하는 것이 좋습니다.
Q 9.5) 리스트 9.1에서 정의된 Circle 클래스를 사용하였을 때 다음 코드의 출력은 무엇인가?
Circle c1(5);
Circle c2(6);
c1 = c2;
cout << c1.radius << " " << c2.radius << endl;
A) 6 6
Q 9.6) 다음 코드는 무엇이 잘못되었는가? (리스트 9.1을 사용)
//(a)
int main()
{
Circle c1();
count << c1.getRadius() << endl;
return 0;
}
//(b)
int main()
{
Circle c1(5);
Circle c1(6);
return 0;
}
A) (a)의 경우, 인수없는 생성자가 괄호와 함께 사용되었습니다. 익명 개체와 헷갈릴 수 있기 때문에 인수없는 생성자는 괄호와 같이 사용될 수 없습니다. (b)의 경우, 같은 이름의 객체가 다시 사용되었습니다. 객체 이름은 한번 선언하면 다시 선언될 수 없습니다.
Q 9.7) 다음 코드는 무엇이 잘못되었는가?
class Circle
{
public:
Circle()
{
}
double radius = 1;
}
A) 클래스 안에서 데이터 필드는 직접 초기화될 수 없습니다. 만약 초기화하고 싶다면 생성자를 활용하여 초기화해야 합니다.
Q 9.8) 다음 문장 중 올바른 문장은 어느 것인가?
(1)
Circle c;
(2)
Circle c();
A) 1번
Q 9.9) 다음 두 문장을 서로 독립적인 것으로 가정하는 경우, 올바른 문장인가?
(1)
Circle c;
(2)
Circle c = Circle();
A)
(1)은 인수 없는 생성자를 이용해 객체를 선언한 것이므로 올바릅니다.
(2) 도 인수 없는 생성자로 익명 개체를 만들고 객체c 를 선언해 데이터 필드를 복사한 것이므로 에러는 나지 않습니다.