씹어먹는 C++ 4일차
❗참고 : https://modoocode.com/135
객체와 클래스
- 절차지향적 언어
    - C, Pascal
- 함수(Procedure)를 지향하는 언어
- 프로그램을 설계할 때 중요한 부분을 하나의 함수로 만들어 쪼개서 처리함
 
- 객체지향적 언어
    - C++, Java, Python, C# 등
- 객체 중심?
 
객체란?
- 
    함수의 인자로 구조체를 넘겨주기 와 변수에 해당하는 함수를 호출하기 
- 객체란 변수들과 참고자료들로 구성된 소프트웨어
    - 자신의 상태를 나타내는 변수
- 자신의 행동을 수행하는 함수
 
- 추상화(Abstraction)
    - 객체가 현실 세계에 존재하는 것들을 나타내기 위한 방법
- 변수 및 함수로 추상화
 
- 인스턴스 변수와 인스턴스 메소드
    - 객체에 정의된 변수와 함수를 의미
- 클래스를 이용하여 만들어진 객체
 
- 캡슐화(Encapsulation)
    - 외부에서 인스턴스 변수를 직접 수정하지 않음
- 인스턴스 변수는 인스턴스 함수를 이용하여 수정
- 객체가 내부적으로 어떻게 동작하는지 모르더라도 사용할 수 있도록 함
 
클래스
- 객체의 설계도
- 클래스를 이용하여 실제 객체를 생성
<Class 이름> <클래스의 인스턴스 명>
- 멤버 변수와 멤버 함수
    - 클래스 안에 선언된 변수와 함수
- 클래스는 인스턴스가 생성되어야 실재함
- 멤버 변수와 멤버 함수도 인스턴스가 생성되어야 실재
 
- 클래스와 접근 지시자
    - 접근 지시자는 private, public 등을 의미
- 클래스 내에서의 변수와 함수는 별도 접근 지시자가 없을 시, 기본으로 private로 설정됨
 
생성자와 함수의 오버로딩
함수의 오버로딩
- 
    C언어에서는 하나의 이름을 가지는 함수는 딱 1개 
- 
    C++에서는 하나의 이름을 여러 함수가 사용 가능 - 함수 호출 시 인자를 이용해 함수들을 구별
- 함수 호출 시, 함수 이름은 동일하더라도 인자가 다르면 다른 함수
 
#include <iostream>
void print (int x) { std::cout << "int: " << x << std::endl; }
void print (double x) { std::cout << "double: " << x << std::endl; }
int main () {
    int a = 1;
    char b = 'c';
    double c = 3.2f;
    print(a);
    print(b);
    print(c);
    return 0;
}
- C++ 컴파일러가 함수를 오버로딩 하는 과정
    - 자신과 타입이 정확하게 일치하는 함수를 찾는다
- 정확하게 일치하는 함수가 없는 경우, 형변환을 통해 일치하는 함수를 찾는다
- 포괄적인 형변환 수행 후 일치하는 함수를 찾는다
- 유저 정의된 타입 변환으로 일치하는 함수를 찾는다
 
생성자
- 객체 생성 시 자동으로 호출되는 함수
- 객체를 생성 후 바로 초기화
// 객체를 초기화 하는 역할
// 별도 반환값이 없음
/* 클래스 이름 */ (/* 인자 */) {}
- example
Date(int year, int month, int day)
Date day(2021, 6, 25);  // 암시적 방법 (implicit)
Date day = Date(2021, 06, 25);  // 명시적 방법 (explicit)
Default constructor
- 생성자를 별도로 정의하지 않더라도 디폴트 생성자가 호출됨
- 인자를 하나도 가지지 않는 생성자
- 클래스에서 개발자가 명시적으로 생성자를 정의하지 않은 경우, 컴파일러가 자동으로 추가해주는 생성자
- 
    단, 개발자가 별도의 생성자를 추가하는 경우, 컴파일러는 디폴트 생성자를 호출하지 않음 
- 
    ※주의사항 : 인자가 없는 생성자를 호출하기 위해서는 “Date day();”가 아닌, “Date day;”로 작성해야 함 
- 개발자가 Default constructor를 정의하는 방법 예)
#include <iostream>
class Date {
  int year_;
  int month_;
  int day_;
public:
  void ShowDate();
  Date() {
    year_ = 2021;
    month_ = 6;
    day_ = 25;
  }
};
void Date::ShowDate() {
  std::cout << "오늘은 " << year_ << "년 " << month_ << "월 " << day_ << "일 입니다." << std::endl;
}
int main() {
  Date day = Date();
  Date day2;
  day.ShowDate();
  day2.ShowDate();
  return 0;
}
- 명시적으로 Default constructor 사용하기
    - C++ 11 이상부터 지원
 
class Test {
  public:
    Test() = default;  // Default Constructor를 정의하라는 의미
}
Constructor overloading
- 생성자 정의 시, 호출 인자에 차이를 주어 오버로딩 가능
#include <iostream>
class Date {
  int year_;
  int month_;
  int day_;
public:
  Date() {
    year_ = 2021;
    month_ = 6;
    day_ = 25;
  }
  Date(int year, int month, int day) {
    year_ = year;
    month_ = month;
    day_ = day;
  }
}
복사 생성자와 소멸자
- new 와 malloc의 차이
    - new로 객체를 동적 할당하는 경우, 객체 생성 후 생성자를 자동 호출
 
소멸자 (Destructor)
- 생성한 객체가 delete될 때, 자동으로 호출되는 함수
- Default destructor도 존재 (아무런 작업 없음)
- 소멸자는 함수의 인자가 없음
- 오버로딩도 되지 않음
- 활용 예
    - 객체 내에서 동적 할당된 메모리 해제
- 쓰레드 사이의 lock 제어
 
~(클래스의 이름)
- 소멸자 규칙
    - 소멸자의 이름 : ~클래스 이름
        - 클래스에 1개만 존재할 수 있음
 
- 매개변수가 없음
- 반환값이 없음
- 소멸자는 명시적으로 호출하는 경우가 없다.
 
- 소멸자의 이름 : ~클래스 이름
        
- 생명 주기
    - 객체가 생성되는 시점에 생성자를, 소멸되는 시점에는 소멸자를 호출한다.
 
- C++ RAII 디자인 패턴 (Resource Acquisition Is Initialization)
    - 클래스 객체의 생명 주기 (생성자와 소멸자를 이용한)
        추가 공부 필요 참고 : RAII 
 
- 클래스 객체의 생명 주기 (생성자와 소멸자를 이용한)
        
복사 생성자
T(const T& a);
- 다른 클래스 T의 객체 a를 상수 레퍼런스로 받음
- 
    a가 const이므로 복사 생성자 내부에서는 a의 데이터를 수정 불가, 값의 복사만 가능 
- 주의 : 아래 두 가지 코드는 서로 다른 의미를 가짐
    - 생성자는 객체가 생성될 때만 호출됨
 
T t1(1, 2);
T t2(t1);
T t3 = t1;
- 복사 생성자가 호출 (t1 -> t2, t3)
T t1(1, 2);
T t2;
t2 = t1;
- 
    t2에 t1 대입 연산 
- 
    C++ 컴파일러는 Default copy constructor를 지원한다 - 단, Default constructor, Default Destructor와 다르게 ‘복사’를 수행
 
Default copy constructor의 한계
- 얕은 복사 (shallow copy)만 지원
    - 복사된 객체가 같은 동적 할당된 메모리를 가리키는 경우, 소멸자에서 해당 메모리를 delete 하면 문제가 발생됨
 
- 
    깊은 복사 (deep copy)가 필요한 경우, 개발자가 따로 복사 생성자를 정의해아함 
- 깊은 복사 (deep copy)
    - 객체의 실제 값(value)를 복사
- 객체를 복사할 때, 해당 객체와 인스턴스 변수까지 복사
- value를 복사하여 새 주소에 저장하므로 참조가 공유되지 않음
 
- 얕은 복사 (shallow copy)
    - 객체의 참조값(주소값)을 복사
- 복사된 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조
- 얕은 복사된 객체의 메모리 주소값이 변경되면 원본 / 복사 객체의 인스턴스 변수 값이 같이 변경됨
 
생성자의 초기화 리스트 (Initializer list)
- 생성자 이름 뒤에 “:” 와 함께 나타남
- 
    생성자 호출과 동시에 멤버 변수를 초기화 해줌 
- 생성자 초기화 리스트 사용
(생성자 이름) : var1(arg1), var2(arg2) {}
- 사용 예제 )
#include <iostream>
class Test {
  int a;
  int b;
  int c;
public:
  Test();
  Test(int a, int b);
  void show_values();
};
Test::Test() : a(10), b(20), c(30) {}
Test::Test(int a, int b)
    : a(a), b(b) {}  // c 값은 설정하지 않음
void Test::show_values() {
  std::cout << a << " | " << b << " | " << c << std::endl;
}
int main() {
  Test t1;
  Test t2(1, 5);
  t1.show_values();
  t2.show_values();
}
-- 실행 결과 --
10 | 20 | 30
1 | 5 | 0
- 생성자와 초기화 리스트의 차이
    - 생성자는 생성 후 초기화를 수행 like “int a; a = 10;”
- 초기화 리스트는 생성과 초기화를 동시에 수행 like “int a = 10;”
- 초기화 대상이 class라면?
        - 생성자의 경우, 복사 생성자가 호출됨
- 초기화 리스트의 경우, 디폴트 생성자가 호출된 후 대입을 수행함
 
-  레퍼런스와 상수 : 생성과 동시에 초기화 되어야 함
        - class 내부에 레퍼런스 변수나 상수가 포함될 경우, 반드시 초기화 리스트를 사용
 
 
- 생성자와 초기화 리스트
Test::Test() {
  a = 10;
  b = 20;
  c = 30;
}
Test::Test() : a(10), b(20), c(30) {}
- 무조건 초기화 리스트를 사용해야 하는 경우,
#include <iostream>
class Test{
  int a;
  int b;
  const int c;
public:
  Test();
  Test(int a, int b);
  Test(int a, int b, int c);
  void show_values();
};
Test::Test()
  : a(10), b(20), c(30) {}
Test::Test(int a, int b)
  : a(a), b(b), c(30) {}  // c가 const 이므로 무조건 초기화가 수행되어야 함 
Test::Test(int a, int b, int c)
  : a(a), b(b), c(c) {}
void Test::show_values() {
  std::cout << a << " | " << b << " | " << c << std::endl;
}
int main() {
  Test t1;
  Test t2(1, 5);
  Test t3(100, 200, 300);
  t1.show_values();
  t2.show_values();
  t3.show_values();
}
-- 실행 결과 --
10 | 20 | 30
1 | 5 | 30
100 | 200 | 300