C와 C++은 문법이 같을 뿐 전혀 다른 언어다.
처음에는 C++이 C에 객체지향만 추가된 수준이었지만 많은 패치가 적용된 지금은 전혀 다른 언어라고 보는게 맞다
C++ 11부터 Modern C++라고 부른다
2011년에 큰 패치가 적용되었고 그게 C++ 11이다
3년마다 C++에 새로운 표준안이 공개되고 컴파일러에 반영이 된다
C++14와 C++17은 비교적 작은 변화만 있었지만 C++20에선 또 대규모 업그레이드가 있음
using namespace std;처럼 지정하고 쓰는것은 권장되지 않는다.
std안에 같은 이름의 여러 함수가 있으면 오류가 생길 수 있기 때문
&로 별명만드는 것을 참조자(레퍼런스)라고 부름
레퍼런스는 반드시 초기화를 해야한다(const와 레퍼런스는 초기화 필요)
레퍼런스가 한번 어떤 변수의 참조자가 되면 더이상 다른 변수를 참조 할 수 없음
=> another_num = b; 해봣자 b의 값이 대입될 뿐이니까
이건 C에선 없다가 C++에서 추가됐나봄
참조자에서 int의 참조자를 만들려면 int&을, double의 참조자는 double&를 쓰는것은 익숙한데
int*처럼 포인터변수의 참조자도 만들 수 있음 int*&로 쓰면 됨
클래스로 만들어진 객체를 인스턴스라고 부름
클래스 내의 변수는 멤버변수, 클래스 내의 함수는 멤버함수
객체의 변수는 인스턴스 변수, 객체의 함수는 인스턴스 함수라고 부름
헤더파일 작성시 #pragma once가 있으면 ifndef~endif 구문을 안써줘도 된다
같은 기능을 해준다. 다만 구버전 컴파일러에서 적용 안될 수 잇어서 그때만 ifndef 쓰면 될듯
프로그램이 운영체제로부터 할당받는 메모리공간은 대표적으로 4가지
1. 코드영역 : 실행할 프로그램의 코드
2. 데이터영역 : 전역변수, 정적변수(extern, static)
3. 스택영역 : 지역변수, 매개변수(컴파일시에 크기가 결정됨, 높은 주소에서 낮은 주소 순으로 할당)
4. 힙영역 : 동적할당영역(런타임에 크기가 결정됨, 낮은 주소에서 높은 주소 순으로 할당)
함수의 오버로딩
리턴과 함수명이 같아도 매개변수가 다르면 다른 함수 취급
디폴트 생성자를 직접 구현해서 값을 넣게 초기화 할 수도 있다
Date()
{
year = 2022;
month = ~~
} 이런식으로 기본 생성자로 만들어도 값이 들어있게 초기화 가능
생성자를 하나라도 정의하는 순간 디폴트생성자는 생성되지 않게 되서 다 구현해줘야 하는 불편함이 있었는데
class Test
{
public :
Test() = default;
}; 이런식으로 디폴트생성자를 만들어라 라고 명시할 수 있음
객체를 동적할당으로 만들면 객체가 필요없어질때 delete하면 되지만
객체를 만들때 객체의 생성자 내에서 동적할당을 한다면 소멸자에서 그걸 delete 해줘야한다
그러니 생성자에서 동적할당을 하면 -> 소멸자에서 해제해줘야해 라고 반응 와야함
복사생성자
그냥 대입만 해주는걸 얕은 복사
메모리를 새로 할당해서 복사하는 것을 깊은 복사
디폴트생성자는 얕은복사만 하게끔 되어있으니
생성자 내에서 동적할당으로 메모리를 직접 할당받아서 사용한다면 복사할때 동적할당도 또 해줘야하니
깊은복사를 해줘야함. 이때 복사생성자 사용
초기화리스트
(생성자 이름) : var1(arg1), var2(arg2) {}
Marine::Marine() : hp(50), coord_x(0), coord_y(0), damage(5), is_dead(false) {}
초기화리스트를 사용하면 생성과 초기화를 동시에 한다(마치 int a=10;처럼)
초기화리스트를 사용하지 않으면 생성을 먼저 하고 나서 대입을 한다.( 마치 int a; a=10;처럼)
자주 쓰이는 기능은 아니지만 클래스에서 유용하게 쓰이는 기능(explicit, mutable)
- explicit
explicit : 명시적인
implicit : 암시적인
즉 explicit을 사용하면 명시적으로만 쓸 수 있기에 암시적으로 쓰는걸 막아주는 역할을 함
만약
MyString(const char* str); 생성자가 있고
func(MyString("abc")); 식으로 인자로 MyString객체를 받는 함수가 있다면
이때 func("abc");라고 사용해도 컴파일러가 가장 비슷한 형태인
func(MyString("abc")); 처럼 암시적으로 변환을 해줌.
이런식으로 암시적 변환이 일어났을때 입력값이 어디 들어갈지 애매해져서
암시적변환을 막고싶으면 explicit 사용
explicit MyString(const char* str); 처럼 하면 해당 생성자로는 암시적 변환이 막힘
- mutable
mutable Cache cache;
처럼 선언하면 const함수 내에서도 cache값을 변경할 수 있음
당연히 const함수는 값을 바꾸지 않는 함수이기 때문에 const를 바꿔준건데
값을 바꿔야 하는 경우 const를 지울 수도 있겠지만
외부에 아무 영향도 주지 않고 내부작업에서 바꾸는 값이 있는 경우라면
외부에서 보기엔 당연히 const가 붙어있어야 할 함수이기에 const를 붙여 둔채로
바꿀 변수쪽에 mutable을 붙이면 const내부에서도 값을 바꿀 수 있음
연산자 오버로딩
(리턴 타입) operator(연산자) (연산자가 받는 인자)
예시) bool operator==(MyString& str);
위 식으로 str1 == str2 라고 쓴다면
이는 내부적으로 str1.operator==(str2)로 처리함
atoi함수
atoi("abc") : 0
atoi("123") : 123
atoi("123abc") : 123
atoi("abc123") : 0
첫 문자부터 확인해서 숫자가 아닌게 나올때까지 읽어서 반환해줌
friend 키워드
A 클래스 내부에 friend class B;처럼 다른 클래스나 함수를 friend로 지정할 수 있음
위처럼 하면 B클래스는 A클래스의 private 요소들에 접근 가능
하지만 반대로 A에서 B의 요소 사용은 안됨 짝사랑임
*a.operator@(b); // 여기선 operator@가 a클래스의 멤버함수로 사용
*operator@(a, b); // 여기선 operator@가 외부에 정의된 일반함수로 사용
컴파일러는 연산자 오버로딩된걸 위 두가지 경우 골라서 사용함
통상적으로 자기자신을 리턴하는 이항연산자 +=라던가 -=같은 애들은 모두 멤버함수로 선언하는것이 원칙
자기자신을 리턴하지 않는 이항 연산자인 + - * / 같은건 외부함수로 선언하는것이 원칙
외부에 일반함수로 선언하고 클래스 내부에 해당 함수를 friend 해주면 됨
명시적변환/암시적변환
static_cast : 흔히 생각하는 일반적인 타입 변환
const_cast : const를 없애는 변환, const int 가 int 로
dynamic_cast : 파생 클래스 사이에서의 다운 캐스팅
reinterpret_cast : 위험을 감수하고 하는 캐스팅, 예를들어 서로 관련이 없는 포인터들 사이의 캐스팅 등
(원하는 캐스팅 종류)<바꾸려는 타입>(무엇을 바꿀 것인가?)
static_cast<int>(float변수); => float을 int로 형변환