클래스(Class) (1)
특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀
자료를 저장하고 자료를 처리할 수 있음
특정한 용도를 수행하기 위한 변수와 함수를 모아 둔 틀
int a, b, c에서 int가 틀이라면 a, b, c 는 틀을 이용하여 찍어낸 변수(객체)
객체(오브젝트) : 틀을 이용하여 찍어낸 개체(변수, 메모리 상의 공간)
접근제어 지시자(access modifier)
객체지향 프로그래밍 언어에서 클래스의 멤버(필드, 메서드)에 대한 접근 권한을 제어하기 위해 사용되는 키워드나 지시자
래스의 내부 구조를 보호하고, 데이터 은닉(data encapsulation)을 지원하여 클래스의 캡슐화를 강화(캡슐화 : 외부에서 접근을 제한하고 내부의 상세 구현을 숨김)
private / public / protected 3가지가 존재
class TV {
private:
bool powerOn;
int channel;
int volume;
// 함수를 사용하여 0 이상의 수만 사용하거나 최대 최소 범위를 설정하자.
public:
void on() {
powerOn = true;
cout << "TV를 켰습니다." << endl;
}
void off() {
powerOn = false;
cout << "TV를 껐습니다. " << endl;
}
void setVolume(int vol) {
if (vol >= 0 && vol <= 100) {
volume = vol;
cout << "볼륨 : " << volume << endl;
}
}
void setChannel(int ch) {
if (ch >= 1 && ch <= 999) {
channel = ch;
cout << "채널 : " << channel << endl;
}
}
// 그런데 그냥 ss.volume = 300 해버리면 막을 방법이 없음. --> private 지시자를 사용하자.
};
void EX1() {
TV lg;
lg.on();
//lg.volume = 50(private 으로 인해 직접적으로 접근 불가)
lg.setChannel(100);
lg.setVolume(33);
lg.off();
}
- class의 경우 접근제어 지시자를 사용하지 않으면 기본적으로 private 이고, struct의 경우에는 public이다.
this 포인터
- 자신이 소속되어 있는 객체의 주소
#include <iostream>
using namespace std;
class MyClass {
public:
// 객체 하나 하나 마다 함수를 따로 만드는게 아니라, 하나의 함수를 만들어 놓고, 각각의 개체에따라 호출되는 것이다.
void PrintThis() { // 보이지 않는 매개변수로 this를 항상 가지고 있다.
cout << "나의 주소는 " << this << " 입니다." << endl;
}
void PrintThis2(MyClass *ptr) { // MyClass 객체 b에서 printthis2를 수행하면 b의 주소가 매개변수로 넘어가 실행되는 것을 표현한 함수
cout << "나의 주소는 " << ptr << " 입니다." << endl;
}
};
void EX1() {
MyClass a, b;
cout << "a의 주소는 " << &a << endl;
cout << "b의 주소는 " << &b << endl;
a.PrintThis();
b.PrintThis();
a.PrintThis2(&a);
b.PrintThis2(&b);
}
모든 함수에서는 보이지 않는 매개변수로 this를 항상 가지고 있다.
따라서 PrintThis2와 같이 자신이 속한 객체의 주소를 매개변수로 받는 함수를 만들지 않는다.
생성자와 소멸자
생성자 : 객체가 생성될 때 자동으로 호출되는 함수
소멸자 : 객체가 소멸될 때 자동으로 호출되는 함수
#include <iostream>
using namespace std;
class MyClass {
public: // 아무것도 안적어도 빈 생성자와 소멸자가 있음.
MyClass() { // 생성자
cout << "생성자 호출" << '\n';
}
~MyClass() { // 소멸자
cout << "소멸자 호출" << '\n';
}
};
MyClass globalobj; // global
int main() {
cout << "main function started" << '\n';
cout << "main function end" << '\n';
}
생성자 -> main함수 시작 -> main함수 종료 -> 소멸자
생성자와 소멸자를 명시적으로 안 적어도 빈 생성자와 소멸자가 있음
#include <iostream>
using namespace std;
class MyClass {
public: // 아무것도 안적어도 빈 생성자와 소멸자가 있음.
MyClass() { // 생성자
cout << "생성자 호출" << '\n';
}
~MyClass() { // 소멸자
cout << "소멸자 호출" << '\n';
}
};
void testlocalobj() {
cout << "testlocalobj started" << '\n';
MyClass localobj;
cout << "testlocalobj end" << '\n';
}
int main() {
cout << "main function started" << '\n';
testlocalobj();
cout << "main function end" << '\n';
}
- 지역변수는 순서대로 생성자 호출되고, 지역변수가 속한 함수가 끝나면, 소멸자가 호출됨.
생성자의 대표적인 사용 예시
생성자
- 멤버 변수 초기화
소멸자
- 메모리 해제
#include <iostream>
using namespace std;
class Complex {
public:
Complex() {
real = 0;
imag = 0;
}
Complex(double real_, double imag_) {
real = real_;
imag = imag_;
}
double GetReal() {
return real;
}
double GetImag() {
return imag;
}
void SetReal(double real_) {
real = real_;
}
void SetImag(double imag_) {
imag = imag_;
}
private:
double real;
double imag;
};
int main(void) {
Complex c1;
Complex c2 = Complex(2, 3); // 이 코드랑 아래코드랑 같은 값을 갖는 표현.
Complex c3(2, 3);
Complex c4 = {2, 3};
Complex c5 = Complex{2, 3};
Complex c6{2, 3};
cout << "c1 = " << c1.GetReal() << " , " << c1.GetImag() << '\n';
cout << "c2 = " << c2.GetReal() << " , " << c2.GetImag() << '\n';
cout << "c3 = " << c3.GetReal() << " , " << c3.GetImag() << '\n';
}
생성자와 소멸자 모두 오버라이딩 가능
c1이 생성될 때 Complex() 생성자 실행
c2와 c3의 경우 omplex(double real_, double imag_) 생성자 실행
c4, c5, c6처럼 중괄호를 이용하여 초기화 할 수 있음
복소수를 다루는 class(예시)
#include <iostream>
using namespace std;
// 생성자 : 멤버 변수 초기화
// 복소수(real, imag)
class Complex {
public:
// 생성자도 default 매개변수를 이용할 수 있음
/*
Complex(double real_ = 0, double imag_ = 0) {
real = real_;
imag = imag_;
}
*/
Complex() : real(0), imag(0) {} // { real = 0, imag = 0} 과 같음
Complex(double real, double imag) : real(real), imag(imag) {} // 괄호 real은 매개변수, 밖의 real은 멤버의 real.
double GetReal() {
return real;
}
double GetImag() {
return imag;
}
void SetReal(double real_) {
real = real_;
}
void SetImag(double imag_) {
imag = imag_;
}
private:
double real;
double imag;
};
int main(void) {
int a(5);
Complex c1;
Complex c2 = Complex(2, 3); // 이 코드랑 아래코드랑 같은 값을 갖는 표현.
Complex c3(2, 3);
Complex c4 = {2, 3};
Complex c5 = Complex{2, 3};
Complex c6{2, 3};
cout << "c1 = " << c1.GetReal() << " , " << c1.GetImag() << '\n';
cout << "c2 = " << c2.GetReal() << " , " << c2.GetImag() << '\n';
cout << "c3 = " << c3.GetReal() << " , " << c3.GetImag() << '\n';
}
생성자도 디폴트 매개변수를 이용할 수 있다
중괄호안에서 멤버변수를 초기화 하는 코드를 멤버변수(매개변수) 의 형태로 나타낼 수 있다
이 때 멤버변수의 이름과 매개변수의 이름이 같아도 올바르게 저장된다
콜론 다음 내용을 먼저 실행하고, 괄호 안의 코드를 실행한다
생성자 위임
ex) Time 클래스(시간, 분, 초를 멤버 변수로 가짐)를 만들어 각가 5초, 5분, 2시간 37분 54초의 정보를 담는 객체들을 만든다고 해보자
class Time { public: Time(5) // 5초 Time(5,0) //5분 Time(2, 37, 54); // 2시간 37분 54초
이런식으로 구현하고 싶은데 알다시피 default parameter를 사용하면 왼쪽부터 대입하여 Time(5,0)과 Time(5)는 5시간이 되버린다
생성자를 매개변수 1개일땐 초로, 2개일땐 분, 초, 3개일때 시간 분 초 로 만들기 위해 각각의 생성자를 선언해야 한다
이 때 생성자가 겹치는 걸 줄이기 위해 생성자 위임을 사용한다
#include <iostream> using namespace std; class Time { public: Time() : h(0), m(0), s(0) {} // 콜론 : 다음 내용을 먼저 실행하고, 괄호 안의 코드를 실행. Time(int s_) : Time() { s = s_; } Time(int m_, int s_) : Time(s_) { m = m_; } Time(int h_, int m_, int s_) : Time(m_, s_) { h = h_; } private: int h; int m; int s; }; int main(void) { Time t1; Time t2(5); Time t3(3, 10); Time t4(2, 42, 15); }
- 생성자에서 콜론 다음 내용을 먼저 실행하고, 괄호 안의 코드를 실행한 다는 점을 염두해 두고 코드를 보면 멤버 변수가 생성자를 통해 초기화 되는 과정을 파악할 수 있다.
참조) 두들낙서 C/C++ 강좌
'Programming Language > C++' 카테고리의 다른 글
[C++] 상수형 매개변수와 상수형 메서드 (0) | 2024.02.04 |
---|---|
[C++] 클래스(Class) (2) - 정적 멤버, 정적 메서드 (0) | 2024.01.30 |
[C++] 네임스페이스(namespace) (0) | 2024.01.30 |
[C++] C++ 스타일 함수 (0) | 2024.01.29 |
[C++] C++ 스타일 문법 (0) | 2024.01.28 |