객체
- instance variable(변수)와 instance method로 구성
- 추상화(abstraction) : 컴퓨터 상에서 현실 세계를 100% 나타낼 수 없기 때문에, 적절하게 컴퓨터에서 처리할 수 있도록 바꾸는 것
- 캡슐화(encapsulation) : 외부에서 직접 인스턴스 변수의 값을 바꿀 수 없고 항상 인스턴스 메소드를 통해서 간접적으로 조절하는 것
캡슐화의 장점
객체가 내부적으로 어떻게 작동하는지 몰라도 사용할 줄 알게 된다!!
클래스
- 객체의 설계도
- 클래스를 이용해서 만들어진 객체 = 인스턴스
- Animal:: Aniaml 클래스에 정의된 함수임을 의미
#include <iostream>
class Animal {
private:
int food;
int weight;
public:
void set_animal(int _food, int _weight) {
food = _food;
weight = _weight;
}
void increase_food(int inc) {
food += inc;
weight += (inc / 3);
}
void view_stat();
}; // 세미콜론 잊지 말자!
void Animal::view_stat() {
std::cout << "이 동물의 food : " << food << std::endl;
std::cout << "이 동물의 weight : " << weight << std::endl;
}
int main() {
Animal animal;
animal.set_animal(100, 50);
animal.increase_food(30);
animal.view_stat(); // 130, 60
return 0;
}
접근 지시자
private : 자기 객체 안에서만 접근 가능. 외부에서는 접근 불가능
public : 외부에서 접근 가능
함수 오버로딩 (Overloading)
함수의 이름이 같더라도 인자가 다르면 다른 함수라고 판단
C++ 오버로딩 과정
1. 자신과 티입이 정확히 일치하는 함수를 찾음
2. 정확히 일치하는 타입이 었는 경우 형변환을 통해 일치하는 함수를 찾음
char, unsigned char, short -> int
unsigned short, int -> int, unsigned int
float -> double
enum -> int
3. 좀 더 포괄적인 형변환을 통해 일치하는 함수를 찾음
임의의 숫자 타입 -> 다른 숫자 타입 ex) float -> int
enum ->임의의 숫자 타입
0 -> 포인터 타입, 숫자 타입
포인터 -> void 포인터
4. 유저 정의된 타입 변환으로 일치하는 함수를 찾음
생성자 (Constructor)
- 클래스 이름과 이름이 같은 특별한 메소드
- 객체를 생성할 때 사용
- 오버로딩 적용 가능
// 클래스 이름(인자)
Date(int year, int month, int day)
기본 생성자 (Default constructor)
- 클래스에서 사용자가 어떠한 생성자도 명시적으로 정의하지 않았을 경우에 컴파일러가 자동으로 추가해주는 생성자
- 직접 정의할 수도 있음
- 명시적으로 디폴트 생성자를 사용하도록 명시할 수 있음
Test test; // 기본 생성자가 호출됨
class Test {
public:
Test() = default; // 디폴트 생성자를 정의해라
};
개발자가 깜빡 잊고 생성자를 정의를 안한 것인지, 아니면 정말 디폴트 생성자를 사용하고파서 이런 것인지 알 수 있음
기본 복사 생성자 (default copy constructor)
- 얕은 복사 (shallow copy)가 일어남
- animal2의 필드가 animal1의 필드의 주소와 같은 주소를 가리킴
- 한번 해제된 메모리는 다시 해제될 수 없음
Animal animal2 = animal;
깊은 복사가 필요한 경우 사용자가 직접 복사 생성자를 만들어서 사용해야 함
얕은 복사 (shallow copy) : 단순히 대입만 해주는 것
깊은 복사 (deep copy) : 메모리를 새로 할당해서 내용을 복사하는 것
복사 생성자 (copy constructor)
- Animal의 객체 animal을 상수 레퍼런스로 받음
- = 복사 생성자 내부에서 animal의 데이터를 변경할 수 없고, 새롭게 초기화 되는 인스턴스 변수들에게만 복사 가능
- 오직 생성 시에만 호출 됨
Animal::Animal(const Animal& aniaml) { // 복사 생성자
food = aniaml.food;
weight = animal.weight
}
인자로 받는 변수의 내용을 함수 내부에서 바꾸지 않는다면 앞에 const를 붙여 주는 것이 바람직 함
소멸자 (Destructor)
- C++에서 객체가 delete 될 때 자동으로 호출되는 함수
- 동적으로 할당된 메모리는 자동으로 delete 되지 않음
- 이런 것들이 쌓이고 쌓여 Memory Leak(메모리 누수) 이 발생함 = 소멸자 필요
- 소멸자가 필요 없는 클래스라면 굳이 따로 작성해줄 필요는 없음
// ~(클래스이름)
~Test()
객체가 delete될 때 소멸자를 통해 객체 내 동적 할당된 필드를 delete 해줄 수 있음
= 객체가 동적으로 할당받은 메모리를 해제해주는 역할
초기화 리스트 (initializer list)
- 생성자 호출과 동시에 멤버 변수들을 초기화 ↔ 생성을 먼저 하고 그 다음에 대입
- 초기화 리스트를 사용하는 것이 더 효율적임
- 상수와 레퍼런스의 경우 모두 생성과 동시에 초기화가 되어야 함
- = 클래스 내부에 레퍼런스 변수나 상수를 넣고 싶은 경우, 초기화 리스트를 사용해서 초기화 시켜주어야함
- 괄호 밖 - 멤버 변수, 괄호 안 - 인자
// (생성자 이름) : var1(arg1), var2(arg2) {}
Animal::Animal : food(5), weight(30) {}
Animal::Animal(int f, int w) : food(f), weight(w) {}
Animal::Animal(int food, int weight) : food(food), weight(weight) {}
static 변수
- 클래스의 모든 객체들이 공유하는 변수
- 모든 객체들이 하나의 static 멤버 변수를 사용함
- 정의와 동시에 값이 자동으로 0으로 초기화 됨
- 객체가 없어도 클래스 자체에서 호출 가능함
- 생성자와 소멸자 안에 total_animal_num을 변경하도록하여 편리하게 관리할 수 있음
// static 변수의 초기화
int Animal::total_animal_num = 0;
// 아래와 같은 방식은 불가 (const static의 경우는 가능)
class Animal {
static int total_animal_num = 0;
...
}
// 생성자
Animal::Animal(int food, int weight) : food(food), weight(weight) {total_animal_num++;}
// 소멸자
~Animal(){total_animal_num--;}
static 함수 역시 클래스 전체에 1개 존재하는 함수로, (클래스)::(static 함수) 형식으로 호출
this
객체 자신을 가리키는 포인터 역할
Animal& Animal::eat(int eat_num) {
food -= eat_num;
if (food <= 0) is_hungry = true;
return *this;
}
// 위 코드는 아래 코드와 동일한 의미
Animal& Animal::eat(int eat_num) {
this->food -= eat_num;
if (this->food <= 0) this->is_hungry = true;
return *this;
}
레퍼런스를 리턴하는 함수
#include <iostream>
class A {
int x;
public:
A(int c) : x(c) {}
int& access_x() { return x; }
int get_x() { return x; }
void show_x() { std::cout << x << std::endl; }
};
int main() {
A a(5);
a.show_x(); // 5
int& c = a.access_x();
c = 4;
a.show_x(); // 4
int d = a.access_x();
d = 3;
a.show_x(); // 4
// 아래는 오류
// int& e = a.get_x();
// e = 2;
// a.show_x();
int f = a.get_x();
f = 1;
a.show_x(); // 4
}
위 코드를 하나씩 살펴 보자.
int& c = a.access_x();
c = 4;
a.show_x();
- c는 x의 레퍼런스, 즉 x의 별명을 받음
- c의 값을 바꾸는 것은 x의 값을 바꾸는 것과 동일함
a.access_x() = 3; // a.x = 3; 과 동일
레퍼런스를 리턴하는 함수는 그 함수 부분을 원래의 변수로 치환했다고 생각해도 상관이 없음
int d = a.access_x();
d = 3;
a.show_x();
- int&이 아닌 int로 받음
- 값의 복사가 일어나 d에는 x의 값이 들어감 (d는 x의 별명이 아닌 독립 변수)
int& e = a.get_x();
e = 2;
a.show_x();
- 오류 발생
- 레퍼런스가 아닌 타입을 리턴하는 경우는 '값' 의 복사가 이루어지기 때문에 임시 객체가 생성됨
- 임시객체의 레퍼런스를 가질 수 없음
int f = a.get_x();
f = 1;
a.show_x();
- 값이 복사됨
- 실제 객체 a의 x에는 영향을 미치지 못함
const 함수
변수들의 값을 바꾸지 않고 읽기만 하는, 마치 상수 같은 멤버 함수
// (기존의 함수의 정의) const;
int eat() const;
'언어 > C, C++' 카테고리의 다른 글
json-c 정리 (1) | 2023.12.26 |
---|---|
[C++] Standard Template Library - STL (0) | 2023.08.24 |
[C++] new, delete (0) | 2023.08.14 |
[C++] 참조자(reference) (0) | 2023.08.14 |
[C++] namespace (0) | 2023.08.11 |