클래스와 객체
캡슐화 (encapsulation)
•
객체의 본질적인 특성
•
객체를 캡슐로 싸서 그 내부를 보호하고 볼 수 없게 함
◦
캡슐에 든 약은 어떤 색인지 성분인지 보이지 않고, 외부로부터 안전
•
캡슐화의 목적
◦
객체 내 데이터에 대한 보안, 보호, 외부 접근 제한
객체의 일부 요소는 공개된다
•
객체의 일부분 공개
◦
외부와의 인터페이스 (정보교환 및 통신)를 위해 객체의 일부분 공개
◦
TV 객체의 경우, On/Off 버튼, 밝기 조절, 채널 조정, 음량 조절 등등
C++ 객체는 멤버 함수와 멤버 변수로 구성된다
•
객체는 상태(state)와 행동(behavior) 으로 구성
•
TV 객체 사례
◦
상태
▪
0n/Off 속성 - 현재 작동 중인지 표시
▪
채널 - 현재 방송중인 채널
▪
음향 - 현재 출력되는 소리크기
◦
행동
▪
켜기
▪
끄기
▪
채널 증가
▪
채널 감소
▪
음량 증가
▪
음량 줄이기
C++ 클래스와 C++ 객체
•
클래스 (붕어빵 틀)
◦
객체를 만들어내기 위해 정의된 설계도, 틀
◦
클래스는 객체가 아님, 실체도 아님
◦
멤버 변수와 멤버 함수 선언
•
객체 (붕어빵)
◦
객체는 생성될 때 클래스의 모양을 그대로 가지고 탄생
◦
멤버 변수와 멤버 함수로 구성
◦
메모리에 생성, 실체(Instance) 라고도 부름
◦
하나의 클래스 틀에서 찍어낸 여러 개의 객체 생성 가능
◦
객체들은 상호 별도의 공간에 생성
C++ 클래스 만들기
•
클래스 작성
◦
멤버 변수, 멤버 함수로 구성
◦
클래스 선언부, 클래스 구현부로 구성
•
클래스 선언부 (class declaration)
◦
class 키워드를 이용하여 클래스를 선언
◦
멤버 변수, 멤버 함수 선언
▪
멤버 변수는 클래스 선언 내에서 초기화할 수 있음
▪
멤버 함수는 원형 형태로 선언
◦
멤버에 대한 접근 권한 지정
▪
private, public, protected 중의 하나
▪
디폴트는 private
▪
public : 다른 모든 클래스나 객체에서 멤버의 접근이 가능함을 표시
•
클래스 구현부 (class Implementation)
◦
클래스에 정의된 모든 멤버 함수 구현
class Circle {
public:
int radius; // 멤버 변수
double getArea(); // 멤버 함수
}
// 클래스 구현부
double Circle :: getArea() {
return 3.14 * radius * radius;
}
C++
복사
예제)
#include <iostream>
using namespace std;
class Circle {
public:
int radius;
double getArea();
};
double Circle::getArea() {
return 3.14 * radius * radius;
}
int main() {
Circle donut; //객체 도넛 생성
donut.radius = 1; // 도넛 객체의 반지름을 1로 생성
double area = donut.getArea(); // 도넛 객체의 면적 알아내기
cout << "donut 면적은 " << area << endl;
Circle pizza;
pizza.radius = 5;
area = pizza.getArea();
cout << "pizza 면적은 " << area << endl;
}
C++
복사
객체 생성 및 활용 설명
•
객체 이름 및 객체 생성
Circle donut;
C++
복사
•
객체의 멤버 변수 접근
donut.radius = 1;
C++
복사
•
객체의 멤버 함수 접근
double area = donut.getArea();
C++
복사
예제2)
#include <iostream>
using namespace std;
class Rectangle {
public:
int width;
int height;
double getArea();
};
double Rectangle::getArea() {
return width * height;
}
int main() {
Rectangle rect;
rect.width = 3;
rect.height = 5;
cout << "사각형의 면적은 " << rect.getArea() << endl;
}
C++
복사
생성자 (Constructor)
•
객체가 생성되는 시점에서 자동으로 호출되는 멤버 함수
•
클래스 이름과 동일한 멤버 함수
생성자 함수의 특징
•
생성자의 목적
◦
객체가 생성될 때 객체가 필요한 초기화를 위해
▪
멤버 변수 값 초기화, 메모리 할당, 파일 열기, 네트워크 연걸 등
◦
생성자 이름
▪
반드시 클래스 이름과 동일
◦
생성자는 리턴 타입을 선언하지 않는다
▪
리턴 타입 없다. void 타입도 안된다
◦
객체 생성 시 오직 한번만 호출
▪
자동으로 호출됨, 임의로 호출할 수 없음
▪
각 객체마다 생성자 실행
◦
생성자는 중복 가능
▪
생성자는 한 클래스 내에 여러 개 가능
▪
중복된 생성자 중 하나만 실행
◦
생성자가 선언되어 있지 않으면, 기본 생성자 자동으로 생성
▪
기본 생성자 - 매개변수 없는 생성자
▪
컴파일러에 의해 자동 생성
예제 - 생성자)
#include <iostream>
using namespace std;
class Circle {
public:
int radius;
Circle(); // 매개변수 없는 생성자
Circle(int r); // 매개변수 있는 생성자
double getArea();
};
Circle :: Circle() {
radius = 1;
cout << "반지름 " << radius << " 원 생성" << endl;
}
Circle :: Circle(int r) {
radius = r;
cout << "반지름 " << radius << " 원 생성" << endl;
}
double Circle :: getArea() {
return 3.14 * radius * radius;
}
int main() {
Circle donut; // 매개변수 없는 생성자 호출
double area = donut.getArea();
cout << "donut 의 면적은 " << area << endl;
Circle pizza(30); // 매개변수 있는 생성자 호출
area = pizza.getArea();
cout << "pizza 의 면적은 " << area << endl;
}
C++
복사
생성자가 다른 생성자 호출 (위임 생성자)
◦
여러 생성자에 중복 작성된 코드의 간소화
▪
타겟 생성자와 이를 호출하는 위임 생성자로 나누어 작성
•
타겟 생성자 : 객체 초기화를 전담하는 생성자
•
위임 생성자 : 타겟 생성자를 호출하는 생성자, 객체 초기화를 타겟 생성자에 위임
Circle :: Circle() {
radius = 1;
cout << "반지름 " << radius << " 원 생성" << endl;
}
Circle :: Circle(int r) {
radius = r;
cout << "반지름 " << radius << " 원 생성" << endl;
}
// 여러 생성자에 코드 중복
C++
복사
→ 간소화된 코드
//위임 생성자
Circle::Circle() : Circle(1) { }
//Circle(int r) 의 생성자 호출
//타겟 생성자
Circle::Circle(int r) {
radius = r;
cout << "반지름 " << radius << " 원 생성" << endl;
}
C++
복사
→ 간소화된 코드로 예제 작성
#include <iostream>
using namespace std;
class Circle {
public:
int radius;
Circle(); // 매개변수 없는 생성자
Circle(int r); // 매개변수 있는 생성자
double getArea();
};
Circle :: Circle() : Circle(1) {} // 위임 생성자
Circle :: Circle(int r) { // 타겟 생성자
radius = r;
cout << "반지름 " << radius << " 원 생성" << endl;
}
double Circle :: getArea() {
return 3.14 * radius * radius;
}
int main() {
Circle donut; // 매개변수 없는 생성자 호출
double area = donut.getArea();
cout << "donut 의 면적은 " << area << endl;
Circle pizza(30); // 매개변수 있는 생성자 호출
area = pizza.getArea();
cout << "pizza 의 면적은 " << area << endl;
}
C++
복사
다양한 생성자의 멤버 변수 초기화 방법
1.
생성자 코드에서 멤버 변수 초기화
Point::Point() {x = 0; y = 0;}
Point::Point(int a, int b) {x = a; y = b;}
C++
복사
2.
생성자 서두에 초기값으로 초기화
// 멤버변수 x, y를 0으로 초기화
Point::Point() : x(0), y(0){
}
// 멤버변수 x=a로, y=b로 초기화
Point::Point(int a, int b)
: x(a), y(b) { // 콜론(:) 이하 부분을 밑줄에 써도 됨
}
C++
복사
3.
클래스 선언부에서 직접 초기화
class Point {
// 클래스 선언부에서 x, y를 0으로 직접 초기화
int x = 0, y= 0;
public:
}
C++
복사
예제 3-5, 멤버변수의 초기화와 위임 생성자 활용
#include <iostream>
using namespace std;
class Point {
int x, y;
public:
Point();
Point(int a, int b);
void show() { cout << "(" << x << ", " << y << ")" << endl;}
};
Point::Point() : Point(0, 0){} //위임 생성자
Point::Point(int a, int b) // 타겟 생성자
: x(a), y(b) {}
int main() {
Point origin;
Point target(10, 20);
origin.show();
target.show();
}
C++
복사
기본 생성자
1.
생성자는 꼭 있어야 하는가?
a.
Yes, C++ 컴파일러는 객체가 생성될 때, 생성자를 반드시 호출
2.
기본 생성자란?
•
클래스에 생성자가 하나도 선언되어 있지 않은 경우, 컴파일러가 대신 삽입해주는 생성자
•
매개변수 없는 생성자
•
디폴트 생성자라고도 부름
기본 생성자가 자동으로 생성되는 경우
◦
생성자가 하나도 작성되어 있지 않은 클래스의 경우
▪
컴파일러가 기본 생성자 자동으로 생성
◦
만약 생성자가 하나라도 선언된 클래스의 경우
▪
컴파일러는 기본 생성자를 자동으로 생성하지 않는다
예제 3-6)
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle();
Rectangle(int w, int h);
Rectangle(int length);
bool isSquare();
};
Rectangle::Rectangle() {
width = height = 1;
}
Rectangle::Rectangle(int w, int h) {
width = w; height = h;
}
Rectangle::Rectangle(int length) {
width = height = length;
}
// 정사각형이면 true를 리턴하는 멤버 함수
bool Rectangle::isSquare() {
if (width == height)
return true;
else return false;
}
int main() {
Rectangle rect1;
Rectangle rect2(3, 5);
Rectangle rect3(3);
if (rect1.isSquare())
cout << "rect1 은 정사각형이다." << endl;
if (rect2.isSquare())
cout << "rect2 는 정사각형이다." << endl;
if (rect3.isSquare())
cout << "rect3 은 정사각형이다." << endl;
}
C++
복사
소멸자
◦
객체가 소멸되는 시점에서 자동으로 호출되는 함수
▪
오직 한번만 자동 호출, 임의로 호출할 수 없음
▪
객체 메모리 소멸 직전 호출된다
소멸자 특징
◦
소멸자의 목적
▪
객체가 사라질 때 마무리 작업을 위함
▪
실행 도중 동적으로 할당 받은 메모리 해제
▪
파일 저장 및 닫기
▪
네트워크 닫기 등
◦
소멸자 함수의 이름은 클래스 이름 앞에 ~를 붙인다
Circle::~Circle() {...}
C++
복사
◦
소멸자는 리턴 타입이 없고, 어떤 값도 리턴하면 안됨
▪
리턴 타입 선언 불가
◦
중복 불가능
▪
소멸자는 한 클래스 내에 오직 한 개만 작성 가능
▪
소멸자는 매개 변수 없는 함수
◦
소멸자가 선언되어 있지 않으면, 기본 소멸자가 자동 생성
▪
컴파일러에 의해 기본 소멸자 코드 생성
▪
컴파일러가 생성한 기본 소멸자 → 아무것도 하지않고 단순 리턴만
Circle.h
#include <string>
using namespace std;
class Circle { //클래스 선언부
public: //접근지정자
int radius; //멤버 변수
std::string name;
Circle(); //매개변수가 없는 생성자도 만들어야
Circle(int r); //생성자
Circle(string n, int r);
Circle(string n);
~Circle(); //소멸자
double getArea(); //멤버 함수
void print();
};
C++
복사
Circle.cpp
#include <iostream>
#include "Circle.h"
using namespace std;
//클래스 구현부
double Circle::getArea() {
return 3.14 * radius * radius;
}
void Circle::print() {
// cout << "넓이는 " << getArea() << endl;
cout << name << "의 넓이는 " << getArea() << endl;
}
Circle::Circle() : Circle("", 0) {
// radius = 0;
// name = "";
}
Circle::Circle(int r) : Circle("", r) {
// radius = r;
// name = "";
}
Circle::Circle(string n, int r) : name(n), radius(r) {
// name = n;
// radius = r;
}
Circle::Circle(string n) : Circle(n, 0) {
// name = n;
// radius = 0;
}
Circle::~Circle() {
cout << "반지름" << radius << "원 소멸" << endl;
}
C++
복사
생성자 / 소멸자 실행 순서
▪
객체가 선언된 위치에 따른 분류
•
지역 객체
◦
함수 내에 선언된 객체로서, 함수가 종료하면 소멸된다.
•
전역 객체
◦
함수의 바깥에 선언된 객체로서, 프로그램이 종료할 때 소멸된다.
▪
객체 생성 순서
•
전역 객체는 프로그램에 선언된 순서로 생성
•
지역 객체는 함수가 호출되는 순간에 순서대로 생성
▪
객체 소멸 순서
•
함수가 종료하면, 지역 객체가 생성된 순서의 역순으로 소멸
•
프로그램이 종료하면, 전역 객체가 생성된 순서의 역순으로 소멸
▪
new 를 이용하여 동적으로 생성된 객체의 경우
•
new 를 실행하는 순간 객체 생성
•
delete 연산자를 실행할 때 객체 소멸
접근 지정자
▪
캡슐화의 목적
•
객체 보호, 보안
•
C++ 에서 객체의 캡슐화 전략
◦
객체의 상태를 나타내는 데이터 멤버(멤버 변수) 에 대한 보호
◦
중요한 멤버는 다른 클래스나 객체에서 접근할 수 없도록 보호
◦
외부와의 인터페이슬르 위해서 일부 멤버는 외부에 접근 허용
▪
멤버에 대한 3가지 접근 지정자
•
private
◦
동일한 클래스의 멤버함수에만 제한함
•
public
◦
모든 다른 클래스에 허용
•
protected
◦
클래스 자신과 상속받은 자식 클래스에만 허용
•
멤버 변수 ⇒ private 지정이 바람직하다
함수 호출에 따른 시간 오버헤드
→ 작은 크기의 함수를 호출하면, 함수 실행 시간에 비해, 호출을 위해 소요되는 부가적인 시간 오버헤드가 상대적으로 크다
인라인 함수
•
인라인 함수
◦
inline 키워드로 선언된 함수
•
인라인 함수에 대한 처리
◦
인라인 함수를 호출하는 곳에, 인라인 함수 코드를 확장 삽입
▪
매크로와 유사
▪
코드 확장 후 인라인 함수는 사라짐
◦
인라인 함수 호출
▪
함수 호출에 따른 오버헤드는 존재하지 않음
▪
프로그램의 실행 속도가 개선
◦
컴파일러에 의해 이루어진다
▪
컴파일러가 판단하여 inline 요구를 수용할 지 결정한다
▪
컴파일러는 inline 처리 후, 확장된 C++ 소스 파일을 컴파일 한다
▪
recursion, 긴 함수, static 변수, 반복문 등을 가진 함수는 X
•
인라인 함수의 목적
◦
C++ 프로그램의 실행 속도 향상
▪
자주 호출되는 짧은 코드의 함수 호출에 대한 시간 소모를 줄임
▪
C++ 에는 짧은 코드의 멤버 함수가 많기 떄문
◦
장점 : 프로그램의 실행 시간이 빨라진다
◦
단점 : 인라인 함수 코드의 삽입으로 컴파일된 전체 코드 크기가 증가한다
▪
짧은 코드의 함수를 인라인으로 선언하는 것이 좋다
자동 인라인 함수
◦
클래스 선언부에 구현된 멤버 함수
▪
inline으로 선언할 필요 없다
▪
컴파일러에 의해 자동으로 인라인 처리
▪
생성자를 포함, 모든 함수가 자동 인라인 함수 가능
◦
C++ 구조체
▪
C++ 구조체
•
상속, 멤버, 접근 지정 등 모든 것이 클래스와 동일
•
클래스와 유일하게 다른 점
◦
구조체의 디폴트 접근 지정 - public
◦
클래스의 디폴트 접근 지정 - private
▪
C++ 에서 구조체를 수용한 이유
•
C언어와의 호환성 때문
◦
C의 구조체 100% 호환 수용
◦
C 소스를 그대로 가져댜 쓰기 위해
▪
구조체 객체 생성
•
struct 키워드 생략
구조체 이용하여 헤드파일 구성
#include <string>
using namespace std;
struct StructCircle { //클래스 선언부
private: //접근지정자
int radius; //멤버 변수
public:
StructCircle(int r) {radius = r;} // 구조체의 생성자
double getArea(); //멤버 함수
};
C++
복사
ㅇ