새로 생성한 스크립트
- DynamicCast : 소멸자 순서 확인 / 상속받은 클래스의 소멸자 확인
- Inline : 선언부와 정의부를 따로 생성하지 않고 선언 및 정의를 바로하는 문법 확인
- Template : 템플릿 함수 예시 / 템플릿 특수화 함수 예시(매개변수에 자료형 넣고 실행) / 템플릿 포인터 예시
- TemplateClass : 템플릿 클래스 생성 / 템플릿 클래스 상속 예시
- Stack_Template : 템플릿으로 스택 생성
auto add = [&]()//var 역할을 하는 자료형인데 지역 변수 아니면 사용하면 안된다. / []안의 내용을 함수로 쓸 수 있다. / &는 이름이 없으니 '익명함수'가 된다.
{
printf("a = %d, b = %d\\n", a, b); //[] 안에 쓰인것만 쓰겠다는 뜻이다. / 아무것도 안적으면 아무것도 안쓰겠다는 의미다. / [&]는 모든 변수를 레퍼런스로 쓰겠다는 의미다. / *은 모든 변수의 값을 쓰겠다는 뜻이다. / [a, b]는 위의 a와 b 값을 복사해서 쓰겠다는 의미다. / ()안에 쓰면 매개변수를 받아서 쓰겠다는 의미다.
printf("&a = %p, &b = %p\\n", &a, &b);
return a + b;
};
std::thread t([=](int x)
{
for (int i = 0; i < x; i++)
{
printf("%d\\n", i);
}
}, b); //이전 C++ 람다 했을때 function을 확인하면 같은 형태이니 확인이 가능하다.
t.join();
auto 키워드
- 컴파일러가 선언된 변수 또는 람다 식 매개 변수의 초기화 식을 사용하여 해당 형식을 추론하도록 지시
- 즉, C#의 ‘var’와 비슷한 역할을 하고 있다.
- 반복자(iterator)(for 등)에 쓰는것은 나쁘지는 않다.
- 가능하다면 auto를 남발하지않고 실제 자료형 타입을 사용하는 것이 좋다.
- 오류생겼는데 auto면 찾으러 돌아다녀야 하기 때문이다.
단점
- 포인터를 사용할때 값 형태인지 포인터 형태인지 알 수가 없어 가독성이 좋지 않다. ⇒ auto*처럼 사용할 수 있다. (포인터를 받을 때 선언)
- 마찬가지로 auto&처럼 사용할 수 있다. (참조를 받을 때 선언)
auto func2 = lambda->GetAdd2(30);
int f = func2(20);
printf("f + %d\\n", f); //member원래 값 10 + 매개변수값 30 + 리턴 함수의 매개변수값 30 = 70이 나온다.
다이나믹 캐스트
- 다이나믹 캐스트는 ‘가상화’가 되었을때만 사용이 가능하다.
- 런타임에서 이게 자식클래스에 가상화가 되는지 확인하기 위해? 사용한다.
- RTTI (런타임 어쩌고에 포함되어있다) → 다이나믹 캐스트 말고 3개가 더 있다.
가상화
- 가상 함수가 ‘하나 이상’있어야 가상화가 가능하다. (가상화 조건 외워둬야 한다.)
- 다운 캐스팅을 체크하려면 가상화가 일어나야한다.
- 가상화 했을때는 ‘소멸자도 가상화’를 해줘야한다.
함수의 실행 순서
int Test(int a)
{
int c = 20;
return a + c + 10;
}
int b = Test(10; //Test(10)주소
- int b의 주소 생성
- 10을 넣어서 넘긴다.
- Test를 넣는다.
- Test를 사용해야하니 코드를 ‘점프’ 할 주소를 꺼낸다.
- Test 요소 접근 → 저장된 Test 주소 꺼낸다.
- 10 저장 공간 꺼내서 사용
- a 공간 생성 = 10 저장
- C 공간 생성 = 20 저장
- a와 c를 사용하여 리턴 ⇒ 40 메모리 이외로 ‘임시 공간에 저장한다.
- 리턴하면 ‘지역 변수’는 전부 삭제된다. ⇒ a와 c 공간 삭제
- b로 돌아갈 주소 꺼낸다.
- 임시 공간에 저장된 40을 저장한다.
기타
- ESP , EBP , EIP(다음에 실행할 명령의 주소를 가진다) : 셋다 32비트다. (E : Extended)
- 원래 위의 3개는 원래 16비트 / 8비트의 컴퓨터 용량에서 생성되었다.
- 16비트는 2바이트이고 65536바이트이니 64킬로 바이트여서 이것을 640킬로 바이트로 늘려쓰기 위해 ‘Extend’라는 개념이 생겼다.
- 지금은 64비트를 사용하고 RSP, RBP, RIP (Regist?)를 사용하고 있다.
함수 호출 순서
- 공간 생성
- 값 저장
- 점프해야하면 주소 꺼내고 점프명령 실행
- ESP는 스택이 쌓일수록 같이 움직인다.
- EBP는 -1 위치에서 계속 다린다.
- EIP는 임시공간에서 계속 명령을 받아서 실행한다.
- 스택은 쌓일때는 top먼저 이동하고 저장하고, 뺄때는 먼저 뺀 다음 top을 내린다.
- ESP와 EBP가 같이 -1이 된다면 스택 포인트 공간을 삭제한다. (함수의 실행이 끝났다)
Inline 함수
- 인라인 함수는 위의 함수 실행과 같지 않다.
- 함수 호출 때 컴파일이 함수 내용을 가져온 뒤 실행한다.
- 스택 포인트가 생성되지 않는다.
- 즉, 점프하지 않고 실행한다.
- 용량이 커진다는 단점이 있다.
- 인라인이 되는 조건
- ESP : 함수가 진행하고 있을 때 stack의 제일 아래 부분, 현재 진행 stack 지점, Stack Pointer stack 메모리는 아래로 성장하기 때문에 제일 아래가 제일 마지막이 된다.
- 즉, 스택에 데이터가 이동하면 같이 이동하는 지점이다.
- EBP : 스택의 가장 윗 부분(기준점), Base Pointer
- 즉, 초기 위치와 같고 ESP와 같아지면 스택이 비어있는 상태이니 스택 포인트가 삭제된다.
- EIP : 실행할 명령의 주소다. → Instruction Pointer
- EBP, ESP, EIP
Template
template<typename T>
void Add(T a, T b)
{
}
Add<int>(10, 20); //인라인처럼 함수를 복사해서 점프하지 않고 실행한다.
void Add<T>(T a, T b)
{
}
Add<int>(10, 10); //C#도 함수를 복사해서 점프하지 않고 실행한다.
template<typename T>
void Print(T data)
{
cout << "템플릿 함수 : " << data << endl; //cout : 콘솔로 아웃 시킨다는 의미다. / << : 스트림 기호
}
int i = 10;
Print<int>(10);
string s = "Atents";
Print<string>(s);
template<>
void Print(float data) //매개변수가 float형이면 이게 실행된다.
{
cout << "템플릿 특수화 함수 : " << data << endl;
}
template<typename T>
void Print(T* data) //포인터도 가능하다.
{
cout << "포인터 : " << *data << endl;
}
float f = 3.14f;
Print<float>(f);
int* p = &i;
Print<int>(p);
template<typename T>
class Character
{
public:
void Set(T name)
{
this->name = name;
}
void Print()
{
cout << "name : " << name << endl;
}
virtual void PrintName() = 0; //순수 가상 함수 : 추상클래스가 된다. => Character는 객체 생성이 불가능해진다.
protected:
T name;
};
Character<string> character;
character.Set("Oak");
character.Print();
class Player : public Character<int>
{
public:
void PrintName() override //이거 재정의 안하면 얘도 추상으로 만들어야한다.
{
cout << "Player : " << name << endl;
}
};
class Monster : public Character<string>
{
public:
void PrintName() override //이거 재정의 안하면 얘도 추상으로 만들어야한다.
{
cout << "Monster : " << name << endl;
}
};
Player player; //얘는 <int>로 고정을 시켜서 템플레이트를 안써도 된다.
player.Set(100);
player.PrintName();
Monster monster; //얘는 <ㄴㅅ갸ㅜㅎ>로 고정을 시켜서 템플레이트를 안써도 된다.
monster.Set("Oak");
monster.PrintName();
#define MAX_STACK_COUNT 20 //스택 최개 개수
template<typename T>
class Stack
{
public:
Stack()
{
memset(values, 0, sizeof(T) * MAX_STACK_COUNT); //memset : 메모리를 셋 하는데 (시작주소, 뭘로 설정할거냐, 배열 초기화 갯수) => C++ 의 배열 초기화 방법
}
void Push(T value)
{
assert(top + 1 < MAX_STACK_COUNT); //top + 1이 스택 최대 갯수보다 작은지 확인해야한다.
values[++top] = value; //top를 먼저 올려서 배열공간이 남았는지 확인하고 넣어야한다.
}
T Pop() //얘는 요소를 뺄거니까 반환하는 자료형이 뭔지 모르니 T로 한다.
{
assert(Empty() == false);
T val = values[top--]; //먼저 요소를 빼고 top를 내려야한다.
return val;
}
T Front()
{
return values[top]; //리턴만 만들고 확인하는 용도로 사용한다.
}
bool Empty()
{
return top < 0 ? true : false; //0보다 작으면 비어있으니 true 아니라면 들어있으니 false다.
}
private:
int top = -1; //top of point 라고 한다.
T values[MAX_STACK_COUNT]; //스택 요소들을 저장할 배열
};
Stack<int> stack;
stack.Push(10);
stack.Push(20);
stack.Push(30);
stack.Pop();
stack.Push(40);
stack.Push(50);
while (stack.Empty() == false)
{
printf("%d\\n", stack.Pop());
}
순수 가상 함수
virtual void PrintName() = 0;
- 이렇게 virtual이 붙은 선언 방식을 ‘순수 가상함수’라고 부른다.
- 이렇게 선언된 순수 가상 함수가 있는 클래스를 ‘추상클래스’라고 부른다.
- 이게 선언된 클래스를 상속받은 자식 클래스는 ‘반드시’ 순수 가상함수를 override 해서 재정의 해줘야한다.