void InitArray(int* const InOutArray, const size_t InSize)
{
for (size_t i = 0; i < InSize; i++) {
InOutArray[i] = i;
}
}
위처럼 배열을 초기화하는 함수가 있다고 할 때,
int Array[1000]{};
//버퍼 오버 플로우
InitArray(Array, 2000);
//스택주소기때문에 위에서 이미 2천까지 할당해버려서
// 밑에 새로 생성하면 이미 값이 들어가있음
int Array2[1000];
Array를 크기의 두 배만큼 초기화했다고 하자
그리고 바로 밑에 Array2를 생성하면 Array2도 같은 스택의 메모리를 사용하기 때문에
Array2의 메모리에 이미 초기화된 값이 저장되어 있다.
버퍼 오버플로우가 일어난 것.
Array2를 다시 초기화 시키면 되긴하지만 오버플로우를 걱정해야한다.
std::unique_ptr<int, std::default_delete<int>> ptr;
선언해줄때 두번째 인자에 이런식으로 소멸자를 넣어줄 수 있다.
std::unique_ptr<int, decltype(&CustomDeleterInt)> Unique(new int{ 123 }, CustomDeleterInt);
void CustomDeleterInt(int* InPointer)
{
std::cout << "deleted~" << std::to_string(*InPointer);
delete InPointer;
}
위와 같은 형식이다.
struct FStruct
{
FStruct() = default;
FStruct(const int InValue);
FStruct(const FStruct& InOther)
: Value(InOther.Value)
{
std::cout << "Copy Constructor\n";
}
FStruct(const std::unique_ptr<FStruct>& InOther)
: Value(InOther->Value)
{
}
private:
int Value = 0;
std::function<void()> Function;
};
위와 같은 FStruct 구조체가 있을 때,
std::unique_ptr<FStruct> Unique = std::make_unique<FStruct>(10);
std::unique_ptr<FStruct> UniqueCopy = std::make_unique<FStruct>(Unique);
make_unique에 Unique 포인터를 넣어주면
FStruct(const std::unique_ptr<FStruct>& InOther)
: Value(InOther->Value)
{
}
이 복사생성자가 호출된다.
하지만
std::unique_ptr<FStruct> UniqueCopy = std::make_unique<FStruct>(Unique);
std::unique_ptr<FStruct> UniqueCopy2 = std::make_unique<FStruct>(*UniqueCopy);
make_unique에 *UniqueCopy를 넣어주게 되면 UniqueCopy가 가리키고 있는 FStruct가 들어와
FStruct(const FStruct& InOther)
: Value(InOther.Value)
{
std::cout << "Copy Constructor\n";
}
이 복사 생성자가 호출된다.
마지막으로
std::unique_ptr<FStruct> UniqueTest = std::move(Unique);
move함수를 이용하면
FStruct(const FStruct& InOther)
: Value(InOther.Value)
{
std::cout << "Copy Constructor\n";
}
이 복사 생성자가 호출된다.
c 스타일 함수포인터 게시글
c++ 스타일 함수포인터 및 bind 함수 게시글
예전에 정리해 뒀었다.
기본 꼴이
[]()
{
};
이런식이다.
[] 는 캡쳐를 할 수 있는 데, [ ] 내부에 적은 변수들을 읽어와 사용할 수 있다.
변수를 그냥 적으면 값 복사를 해오고,
레퍼런스를 달면 값 참조가 가능해진다.
만약 아무것도 없이 =만 적으면 [=] 해당 블록의 모든 변수를 값 복사를 해오고,
&만 적으면 [&] 해당 블록의 모든 변수를 참조해온다.
[=,a] 이렇게 적으면 변수 a만 참조하고 나머지 변수는 값 복사를 하고,
[&, a] 이렇게 적으면 변수 a만 값복사를 하고 나머지 변수는 참조해온다.
넣어줄 인자를 적는 부분이다.
[](const int InValue)
이런식이면 해당 람다함수의 parameter로 const int형의 InValue가 넘어온다는 뜻이다.
기본 함수와 똑같이 {} 내부에 함수 내용을 구현하면 된다.
[](const int InValue) -> int
{
int a= InValue;
return a;
};
이렇게 코드가 있을 때 -> int 이부분이 반환을 의미하는 데
기본 자료형처럼 추론가능한 경우 생략해도 알아서 반환한다.
[]()
{
std::cout << "Lambda Function!\n";
}();
이런 식으로 마지막에 ()를 추가해주면 바로 실행이 가능하다.
[ ] 캡쳐시 값 복사를 하게되면 해당 시점의 값이나 객체를 캡쳐한다.
따라서 해당 객체에 나중에 변동이 생긴 후, 람다 함수를 호출하게 되어도,
캡쳐시점의 값을 이용하게 된다.
유의해야한다.
마찬가지로 &을 이용해 참조하게 되면
사용 중 참조중인 객체나 값이 delete하게 되면 댕글링 포인터와 비슷하게
존재하지 않는 값을 참조 중일 수 있으므로 유의해야한다.
std::function Lambda = [a, b, &c](const int InValue)
{
}
이런식으로 <functional>헤더의 function에 람다함수를 넣어주면 알아서 Lambda에 담긴다.
void Function(std::function<void()> InFunction);
이런 식으로 함수 선언 후, 매개변수 타입에 맞는 함수를 넣어줄 수도 있다.
void Test();
Function(Test);
만약 함수를 매개변수로 받아오면 원하는 시점에 호출도 가능하다.
void Function(std::function<void()> InFunction)
{
std::cout << __FUNCTION__ << std::endl;
InFunction();
std::cout << __FUNCTION__ << " end" << std::endl;
}
__FUNCTION__은 함수의 이름을 반환한다.
위와 같이 받아온 함수를 실행 시킬수도 있다.
위에도 말했지만
만약 등록해둔 람다 함수가 늦게 호출되는 경우
( Ex) 스레드, 엔진에서 다른 시점에 호출되는 경우 )
람다로 &나 pointer로 capture 해둔 변수에 접근하는 경우 해당 변수가 유효한지
확인해야 한다.
컴퓨터에서 랜덤을 사용 시, 기본적으로 제공하는 rand()함수가 있지만
문제는 품질이 낮다.
시드값이 동일하면 동일하게 난수가 나온다.
이 말인 즉슨 초기화가 적절히 이루어지지 않으면 난수가 동일하게 생성될 수 있다.
또한
rand()는 0과 RAND_MAX 사이의 정수를 생성하고, RAND_MAX는 보통 32767이며,
필요한 범위의 난수를 얻으려면 추가적인 연산이 필요하다.
이래서 헤더파일 <random>을 사용한다.
//실시간 난수 시드
std::random_device RD;
//Mersenne Twister 생성기
std::mt19937 Gen(RD());
//// 1에서 100 사이의 균등 분포
std::uniform_int_distribution<int> Dist(0, 999);
int RamdomValue = Dist(Gen);
이런 것들을 지원해주는 데,
std::mt19937 Gen(RD());
std::mt19937는 Mersenne Twister 알고리즘을 사용하여 더 고품질의 난수를 생성해준다고 한다.
std::uniform_int_distribution<int> Dist(0, 999);
여기서 이 uniform_int_distribution은 균등 분포를 지원해 준다
std::random_device RD;
여기서 random_device는 고품질의 시드를 얻을 수 있다고 한다.