danbibibi
article thumbnail

객체

  • 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
profile

danbibibi

@danbibibi

꿈을 꾸는 시간은 멈춰 있는 것이 아냐 두려워하지 마 멈추지 마 푸른 꿈속으로