visual studio에서 디버깅 중에 데이터에 중단점을 걸 수 있다.
해당 데이터에 접근할때마다 중단점이 적중된다.
const char* StringA{ "Hello" }; //Data영역
위와 같이 const char* 형으로 선언하면 StringA가 가리키는 영역은 data영역의 리터럴 상수
"Hello"이다.
const char StringB[]{ "Hello" }; //Stack영역
하지만 위처럼 const char 형으로 배열을 선언하면 그냥 Stack영역에 배열이 생긴다.
여기서
if (StringA == StringB)
이런 식으로 비교해버리면 서로의 주솟값을 비교하는 꼴이기때문에
서로 다르다고 처리된다.
bool bSame = true;
for (int i = 0; i < 5; i++)
{
if (StringA[i] != StringB[i])
{
bSame = false;
break;
}
}
위와 같은 방식으로 한글자씩 비교해볼 수 도 있다.
strcmp(StringA, StringB);
아니면 strcmp함수를 이용해 반환값이 0이면 같은 문자열 0이 아니라면 다른 문자열값이다.
std::string AA{ "Wow!" };
std::string BB{ "Wow!" };
if (AA == BB)
{
std::cout << AA._Equal(BB);
}
std::string형이라면 위처럼 ._equal함수를 사용하거나 연산자 오버로딩된 operator==을 사용하면 된다.
wstring도 string처럼 find를 사용할 수 있고,
string처럼 못 찾고 끝에 다다르면 end()를 반환하는게 아니라 std::wstring::npos를 반환한다.
int형으로 변경해주려면
std::atoi()
std::stoi()
위 두 함수가 이용된다.
atoi는 char형 문자열을 변경해주는 함수고, stoi는 string형 문자열을 변경해주는 함수다
int를 string으로 변형하려면
std::to_string을 사용하면 되는데
wstring일 경우 std::to_wstring을 사용하면 된다!
std::stof()
함수도 존재한다.
std::wstring String = TEXT("3,14f");
float Float = std::stof(String);
std::wstring String2 = std::to_wstring(Float);
이런식으로 wstring을 float으로 변경 후, 다시 wstring으로 변경할 수 있다.
String_view는 c++17에 추가된 읽기전용 string class이다
std::wstring String{TEXT("Hello")};
// 복사가 발생!
std::wstring String2 = String;
위처럼 그냥 대입연산자를 사용하면 복사가 발생한다.
서로 영향을 안 미친다.
복사가 발생하지 않고 참조하게 하는 방법은 여러가지가 있다.
std::wstring& StringReference = String;
std::wstring* const StringPointer = &String;
위와 같이 레퍼런스로 넣어주거나 포인터로 접근하게 하면 참조가 생긴다.
또한 복사가 발생하지 않게 하기 위해 String_view를 사용할 수 있다.
// 복사가 발생 하지 않음
std::wstring_view WStringView = String;
std::wstring_view WStringView2 = TEXT("Hello");
위 코드처럼 wstring_view클래스를 이용해 string클래스를 받으면 복사가 생성하지 않는다
이 string_view의 특징은 함수 인자로 받아올 때 차이가 있는 데,
아래와 같은 람다함수가 있다고 하자.
auto Function1 = [](std::wstring InString)
{
};
auto Function2 = [](std::wstring_view InStringView)
{
std::wcout << InStringView << std::endl;
};
auto Function3 = [](const std::wstring& InString)
{
std::wcout << InString << std::endl;
};
Function1(TEXT("asdasd"));
Function2(TEXT("asdasd"));
Function3(TEXT("asdasd"));
위처럼 함수에 그대로 wstring형 문자열을 넣는다고 해보자.
Data영역에 있던 asdasd가 const wstring& 로 변환되기 위해서는
wstring 임시객체가 만들어 져야 한다.
하지만 wstring_view를 사용하게되면 불필요한 복사를 안해도 된다.
void operator=(const FClass& InOhter)
{
a = InOhter.a;
a2 = InOhter.a2;
a3 = InOhter.a3;
}
위처럼 그냥 void를 이용해서 대입연산을 하게 되면,
class A = class B
이구조는 가능하지만 연쇄대입이 안된다(ex) A = B = C)
const FClass& operator=(const FClass& InOhter)
{
a = InOhter.a;
a2 = InOhter.a2;
a3 = InOhter.a3;
return *this;
}
이런식으로 클래스를 반환해야한다.
클래스의 멤버함수에 const키워드가 달린 건 해당 멤버함수에서
this 포인터를 호출할때 해당 포인터가 const타입이라는 뜻이다.
따라서 const 멤버함수에서는 this 포인터가 const 타입이므로
멤버변수변경이나 const가 아닌 다른 멤버 함수 호출은 불가능하다.
물론 const멤버 함수에서도 static함수는 this포인터를 사용하지 않기 때문에 호출 가능하다.
static키워드로 선언된 멤버 변수나 멤버 함수는 data영역에 올라간다.
따라서 클래스의 인스턴스를 거치지 않고 일반 함수처럼 사용할 수 있다.
인스턴스를 거치지 않기 때문에 this 포인터를 아예 사용하지 않으므로
static이 아닌 멤버 변수나, static이 아닌 다른 멤버함수를 호출할 수가 없다.
자주 보는 오류인
"비 정적 함수는 특정 개체에 상대적이여야 합니다."의 의미는
static이 아닌 함수는 특정 instance에 묶여야한다. 이런 의미인 것 같다.
여기서 생각해보면 한가지 신기한 사실을 알 수 있는데
FClass2 라는 클래스를 가리키는 포인터가 nullptr이라고 해보자
FClass2* Class = nullptr;
이 클래스의 멤버 함수로 HelloTest()와 Hello()가 존재한다
void HelloTest() const
{
std::cout << std::format("Hello : \n");
}
void Hello() const
{
//a는 멤버변수
std::cout << std::format("Hello : {}\n", a);
}
이런 식으로 존재할 때,
nullptr인 Class에서 이 두 함수를 호출하게 되면 어떨까
HelloTest는 문제없이 실행되지만
Hello()에서 오류를 던진다.
바로 멤버 변수 a때문인데,
a를 호출하게되면 this->a 이런식으로 불리게되는데
클래스포인터가 nullptr이므로 this가 nullptr이된다.
따라서 nullptr에 참조하게 되는 꼴이므로 오류가 난다.
따라서 nullptr이라고 해도 무조건 오류가 나는게 아니라
this포인터에 참조를 해야 오류가 생긴다!