반응형

C++ 코드를 보다보니 [[ ~ ]] 와 같은 형식으로 작성된 부분 있었다.

 

뭔가 싶어서 검색해보니 컴파일러에게 코드에 대해서 좀 더 명확하게 힌트를 제공하는 기능이라고 한다.

 

그중에서 자주 사용할것 같은 attribute만 간단하게 정리해보고자 한다. (개인적인 기준;;)

 

1. [[nodiscard]] - C++17

 

이 attribute가 붙은 함수나 attibute가 붙은 enum, class등을 리턴하는 함수를 만들었다면

해당 함수를 호출할때는 리턴값을 받아야 한다고 컴파일러에 알려주는 것이다.

이는 컴파일 시점에 워닝으로 알려준다.

 

[[nodiscard]] int strategic_value(int x, int y) { return x^y; }

int main()
{
  strategic_value(); // warning
  auto v = strategic_value(); // ok

  return 0;
}

 

[[nodiscard("string"]] - C++ 20은 워닝 메시지에 "string" 부분도 같이 남겨준다.

 

2. [[deprecated]] / [[deprecated("string") - C++14

 

해당 attribute가 붙은 함수를 사용하면 사용은 가능하지만 앞으로 제거될 수 있음을 워닝으로 알려준다.

엔진이나 라이브러리를 만들때 많이 사용될 것 같다.

언리얼 엔진에서도 버전업 하고 빌드해보면 나올때가 있다. 그러면 최신에 맞게 코드를 수정해주곤 한다.

 

[[deprecated]]
void TriassicPeriod() {
    std::clog << "Triassic Period: [251.9 - 208.5] million years ago.\n";
}
 
int main()
{
    TriassicPeriod(); // warning
    return 0;
}

 

3. [[fallthrough]] - C++17

 

switch / case 문을 사용하다보면 fallthrough 기능을 사용하곤한다.

컴파일러에 따라서 해당 부분을 워닝처리할때가 있는데 컴파일러에 명확하게

내가 fallthrough를 한거니까 워닝처리를 하지 말라고 하는 것이다.

 

void f(int n) {
  void g(), h(), i();
  switch (n) {
    case 1:
    case 2:
      g();
     [[fallthrough]];
    case 3: // no warning on fallthrough
    ..
  }
}

몇가지 더 있기도 하고 추가될 예정인것 같지만 일단 이정도만 알고 사용해도 되지 않을까 싶다.

반응형
Posted by msparkms
,
반응형

std::string을 사용하면서 printf처럼 formatting 기능을 사용하고 싶었다.

혹시 새로운 기능이 없을까 하고 검색해보니 c++20에 std::format이라는 기능이 추가되었다고 한다.

std::format은 아래와 같이 std::string_view를 인자로 받는다.

template<class... Args>
string format(string_view fmt, const Args&... args);

string_view이기 때문에 그냥 const char*를 넘겨도 된다.

 

기본 사용방법은 아래와 같다.

std::format("{} {}!", "Hello", "world");	// Hello world!

{} 위치에 배치된다고 보면 된다.

 

{} 이곳에 들어갈 여러가지 기능들은 너무 많아서 링크로 대신한다.

필요할때 마다 검색해서 사용해봐야 될 것 같다.

https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification

 

std::formatter - cppreference.com

template struct formatter; (since C++20) The enabled specializations of formatter define formatting rules for a given type. Enabled specializations meet the Formatter requirements. In particular, they define callables parse and format. For all types T and

en.cppreference.com

 

format을 사용하기 위해서는 Visual Studio 2019를 16.10 버전 이상으로 올려야 하고

C++ 버전은 latest로 변경해야 한다.

 

Vector3를 String으로 변경하는 간단한 코드를 예제로 남기고 마무리하면 될 것 같다.

class Vector3
{
public:
  float x_, y_, z_;
  
public:
  std::string toString()
  {
    return std::format("( {:.2f}, {:.2f}, {:.2f} )", x_, y_, z_);
  }
};
반응형
Posted by msparkms
,
반응형

C++은 메모리를 직접 관리할 수 있는 언어이다.

직접 관리는 자유를 주는 대신 책임도 따른다.

메모리를 직접 관리하면 실수에 의해 누수가 일어날 수 있다.

이를 막기 위해 가비지 컬렉션등 메모리 관리를 하는 알고리즘을 구현하거나 라이브러리등을 가져와야한다.

 

C++의 스마트 포인터를 이용하면 포인터를 사용하는 것 처럼 사용할 수 있으면서 (연산자 오버로딩)

메모리 관리는 스마트 포인터 객체에게 맡길 수 있다.

 

스마트 포인터 종류를 간단히 정리해보자.

보통 메모리 누수가 발생하는 이유는 해당 메모리를 바라보는 포인터들이 여러개일 경우

맨 마지막에 해당 메모리에 접근한 포인터가 메모리를 해제해야하는데 이를 관리하기가 쉽지 않다.

 

그래서 사용하는 전략이 대표적으로 두가지가 있는데

해당 메모리를 바라보는 포인터는 무조건 하나만 존재하게 만드는 고유 소유권 방식과

해당 메모리를 바라보는 포인터가 생기면 카운트를 올려주고 해제하면 카운트를 내려서

카운트가 모두 제거되었을때 실제 메모리를 제거해주는 레퍼런스 카운팅 방식이 있다.

 

다른 이유는 delete를 하기전에 예외등이 발생하여 delete가 실행이 안되는 경우이다.

이때는 스마트 포인터 객체로 감싸져 있기 때문에

예외가 발생했을때 해당 객체가 제거되고 메모리 해제부분이 실행되어 해결이 된다.

 

unique_ptr은 고유 소유권 방식의 스마트 포인터이다.

C++14부터 잘 지원되는 auto와 std::make_unique()를 이용하면 쉽게 unique_ptr을 생성할 수 있다.

make_unique()를 이용하면 생성자의 인자를 바로 넘겨줄 수 있어 간단하고

한번 감싸져 있는 형태여서 예외가 발생했을때 누수가 발생할 염려도 없다.

 

기본적인 사용법은 아래와 같다.

기본적으로 포인터와 같은 연산자를 이용할 수 있다.

get()을 통해 내부의 포인터를 직접 얻어올 수 있다.

reset()을 통해 내부 포인터를 해제하거나 내부 포인터를 다른 포인터로 교체할 수 있다.

release()는 내부 포인터를 돌려주며 소유권을 끊어주어서 직접 해제해야 한다.

class Test
{
public:
	Test(int x) : x_(x) {};

	void Print()
	{
		std::cout << x_ << std::endl;
	}

private:
	int x_;
};

void PrintTest(std::unique_ptr<Test> testPtr)
{
	testPtr->Print();
}

int main()
{
	auto myTest = std::make_unique<Test>(1);
	myTest->Print();

	PrintTest(std::make_unique<Test>(2));

	myTest.get()->Print();		// internal pointer
	myTest.reset();			// to nullptr
	if (!myTest)
	{
		std::cout << "myTest is empty" << std::endl;
	}
	myTest.reset(new Test(3));	// to new pointer
	myTest->Print();

	auto myInternalTest = myTest.release();
	if (!myTest)
	{
		std::cout << "myTest is empty" << std::endl;
	}
	myInternalTest->Print();
	delete myInternalTest;		// manually delete

	return 0;
}
반응형
Posted by msparkms
,
반응형

C++11에 유니폼 초기화가 추가되었다.

{}를 이용한 초기화여서 Brace-Initialization 이라고도 부르는것 같다.

 

C++11에 {}를 이용해서 초기화 문법을 따로 추가한 이유는 무엇일까??

사실 이전부터 일부 상황에서는 {}를 이용한 초기화를 사용하고 있었고

일부 상황에서는 ()를 이용한 초기화를 사용하고 있었다.

 

구조체와 배열의 경우에는 {}를 이용해서 초기화를 하고 있었고

하지만 클래스는 ()를 이용해서 생성자를 호출해서 초기화 하는식으로 처리가 되고 있었다.

struct A
{
  int x, y;
};

class B
{
  public:
    B(int x, int y) : mX(x), mY(y) {}
  private:
    int mX, mY;
};


A a = {10, 20};			// {}
B b(10, 20);			// ()

int c[4] = {1, 2, 3, 4};	// {}

C++11에서는 이 부분을 모두 {}를 이용하는 유니폼 초기화로 문법을 통일시켰다고 보면 될 것 같다.

=을 붙이거나 붙이지 않거나 모두 유니폼 초기화를 사용할 수 있다.

A a1 = {10, 20};
B b1 = {10, 20};

A a2{10, 20};
B b2{10, 20};

유니폼 초기화는 일반 자료형에도 사용 가능하다. 아래 예제에서 {}로 초기화할 경우 0이 들어간다.

int a = 1;
int b(1);

// uniform-initialization
int c = {1};	
int d{1};
int e{};	// 0

기왕 유니폼 초기화 기능을 넣고 문법을 통일하면서 한가지 기능을 더 추가하였는데

바로 축소 변환 방지(narrowing) 기능이다.

이는 암묵적 캐스팅이 진행될 때 기존 데이터의 손실이 발생할 경우 에러를 처리해주는 기능을 말한다.

예를 들자면 int에 double 값을 넣을 경우에 소수부분은 손실되게 되는데 이러한 상황을 막는 것이다.

void PrintInt(int x)
{
  std::cout << "Num : " << x << std::endl;
}

A i { 3.14, 3.14 };	// warning
B j { 3.14, 3.14 };	// error
int k { 3.14 };		// error

PrintInt(3.14);		// warning
PrintInt({ 3.14 });	// error

내가 테스트한 환경에서는 구조체의 경우에는 warning 처리로 끝나기는 했고 나머지의 경우에는 모두 축소 변환 에러가 발생했다. (VS 2019 - C++17)

 

클래스 멤버 변수를 선언하면서 바로 초기화 하거나 초기화 리스트에서 배열을 초기화 할때도 사용할 수 있다.


다음은 직접 리스트 초기화와 복제 리스트 초기화에 대한 부분을 간단히 정리하고 넘어가려고 한다.

위에 예제를 보면 a = {}의 형식이거나 아니면 a {} 형식으로 초기화를 하였다.

 

a = {}와 같은 형태를 복제 리스트 초기화라고 하고

a {}와 같은 형태를 직접 리스트 초기화라고 한다.

 

그냥 생성자 호출과 복사 생성자 호출 (= 연산자) 의 느낌이라고 대충 생각해도 될 것 같다.

 

여기서 이야기할 내용은 {}를 auto로 받았을 경우에 타입이 무엇이냐에 대한 것이다.

{}를 auto로 받으면 initializer_list<>으로 변경되고 처리가 되는데 C++17 이전과 이후에 약간 변화가 있어서 정리하려고 한다.

 

C++17 이전은 모두 initializer_list<>로 처리가 되고 C++17은 복제 리스트 초기화만 initializer_list<>로 처리된다.

// before C++ 17
auto a1 = { 1 };	// initializer_list<int>
auto a2 = { 1, 2 };	// initializer_list<int>
auto a3{ 1 };		// initializer_list<int>
auto a4{ 1, 2 };	// initializer_list<int>
auto a5 = { 1, 1.1 };	// error

// after C++ 17
auto a1 = { 1 };	// initializer_list<int>
auto a2 = { 1, 2 };	// initializer_list<int>
auto a3{ 1 };		// int
auto a4{ 1, 2 };	// error
auto a5 = { 1, 1.1 };	// error

C++17 이후 a4의 경우에는 초기화 리스트가 아닌 타입을 추론하게 되는데

이때 하나의 값에 의한 추론만 진행하기 때문에 값이 여러개라고 에러가 발생하게 된다.

그리고 a5의 경우에는 복제 초기화 리스트라도 다른 타입의 값을 넘긴다면 추론에 실패한다.

 

참고로 내가 테스트한 환경에서는 C++14로 낮춰도 a4 부분이 에러가 발생하기는 했다. (VS2019 - C++14)

 

마지막으로 어떤 타입인지 직접 출력해보고 싶으신 분들은

typeinfo의 typeid를 이용해서 콘솔로 출력해보면 좋을 것 같다.

반응형
Posted by msparkms
,
반응형

C++ 17에 std::string_view가 추가되었다.

기존에 std::string이 있는데 std::string_view가 따로 추가된 이유는 무엇일까?

 

문자열을 다루는 함수를 만든다고 해보면 매개변수를

const char* 혹은 const std::string& 으로 만드는 것이 보통이다.

이 두가지 선택은 각각 장단점을 가지게 된다.

 

const char*를 선택했다면 문자열 리터럴을 포함한 문자열 포인터를 받는데 용이하다.

대신 std::string에서 제공해주는 많은 기능을 사용할 수 없다.

 

const std::string&을 선택했다면 std::string의 기능을 사용할 수 있지만

문자열 리터럴을 받는다면 std::string 객체가 생성된다.

 

함수를 오버로딩해서 const char*, const std::string& 함수를 모두 만들면 해결은 되지만

뭔가 좋아보이지는 않는다.

 

두가지 장점을 모두 가져가기 위해 추가된 것이 std::string_view이다.

std::string_view는 내부에 포인터와 길이정보를 가진다.

그래서 문자열 포인터를 바로 받을 수 있으면서 std::string의 기능을 제공해준다.

포인터와 길이 정보만 가지고 있기 때문에 보통 레퍼런스가 아닌 값을 전달하는 방식으로 구현한다.

using namespace std::string_view_literals;

void StringViewTest(std::string_view strv)
{
    std::cout << "str : " << strv.data() << " length : " << strv.length() << std::endl;
}

StringViewTest("abcdefg");

std::string TestString("Test");
StringViewTest(TestString);

auto sv = "My Test String View"sv;
StringViewTest(sv);

간단한 예제는 위와 같다.

std::string_view를 매개변수로 받는 함수로 문자열 리터럴, std::string, std::string_view를 처리할 수 있다.

 

참고로 using namespace std::string_view_literals;를 추가하면

문자열 뒤에 sv를 붙여줌으로써 string_view 리터럴을 사용할 수 있다.

반응형
Posted by msparkms
,
반응형

연산자 오버로딩의 경우에는 개인적으로는 구현을 해볼 일이 없었다.
엔진을 사용하기 때문에 기본적인 자료형들의 연산자들은 구현이 되어 있었기 때문이다.
최근에 연산자 오버로딩을 하는 부분을 본 것은 stream처리를 하는 부분을 추가할 때 정도였다.
개인적으로는 많이 사용하지는 않기 때문에 간략히 정리해보고자 한다.

항목5 - 사용자 정의 타입변환 함수에 대한 주의를 놓지 말자.

암시적 타입변환은 원하든 원하지 않든 컴파일러에 의해 자동으로 일어난다.
암시적 타입변환을 하는 함수는 두 가지가 있다.

단일 인자 생성자

template<class T>
class Array
{
  public:
    Array(int lowBound, int highBound);
    Array(int size);

    T& operator[] (int index);
    ...
}

bool operator == (const Array<int>& lhs, const Array<int>& rhs);

Array<int> a(10);
Array<int> b(10);

for (int i = 0; i < 10; ++i)
{
  if (a == b[i])
  {
  }
  else
  {
  }
}

a == b[i]는 오타가 나서 컴파일 에러가 나기를 원한다.
하지만 b[i]는 int 이므로 임시로 Array 생성자로 변환된다.

위와 같은 생성자에는 explicit를 넣어주자.

암시적 타입변환 연산자

class Rational
{
  public:
    ...
    operator double() const;
    ... 
}

Rational r;
double d = 0.5 * r;

cout << r;

<< 연산자를 구현하지 않았지만 동작을 한다!
원하는 결과는 아닐 수 있다.

컴파일 에러를 원한다.

이를 막기위해 암시적 타입변환 연산자를 제거하고 같은 기능을 하는 함수를 제공하자.
asDouble()과 같은 함수.

항목6 - 증가 및 감소 연산자와 전위, 후위 형태를 반드시 구분하자.

전위, 후위 증감 연산자는 인자를 넘기지 않는다.
그래서 약속된 형식이 아래와 같다.
후위 ++의 경우에는 숫자 0을 넘기는 식으로 구분하게 되었다.

class UPInt
{
  public:
    UPInt& operator++();                // 전위++
    const UPInt operator++(int);      // 후위++
    ...
}

UPInt& UPInt::operator++()
{
  *this += 1;
  return *this;
}

const UPInt UPInt::operator++(int)
{
  const UPInt oldValue = *this;
  ++(*this);
  return oldValue;
}

후위 연산자의 경우에 const를 리턴하지 않는 경우에는 i++++과 같은 이상한 문법이 가능해진다.
i.operator++(0).operator++(0); 과 같다.
그리고 oldValue와 같은 임시객체가 필요한것도 마음에 들지 않는다.
그렇기 때문에 가능하면 전위 연산자를 사용하도록 하자!
또한 구현의 일치성을 보장하기 위해 후위 연산자에서 위의 예제와 같이 전위 연산자를 호출 해주는게 좋다!

항목7 - &&, ||, 혹은 . 연산자는 오버로딩 대상이 절대로 아니다.

복잡한 조건을 검사할 때 단축처리가 가능하다.
가령 if ((p != 0) && (strlen(p) > 0)) 와 같은 조건이 있다면 p가 0(nullptr)이면 strlen()이 호출되지 않는다.

그런데 우리가 굳이 위의 &&, || 연산자를 재정의 한다면 위의 단축처리가 불가능하다.

if (expression1 && expression2)
-> expression1.operator&&(expression2)
-> operator&&(expression1, expression2)

둘 중의 하나로 해석될 것이기 때문이다.

즉 expression2가 실행이 된다는 이야기다.

, 연산자의 경우에도 마찬가지다.

항목8 - new, delete의 의미를 정확하게 구분하고 이해하자.

new 연산자의 동작

  1. 메모리 할당
  2. 객체의 생성자 호출

오버로딩할 수 있는 부분은 메모리를 할당하는 부분이다.

void* operator new(size_t size);

메모리 지정 new (placement new)

이미 할당된 메모리에 new 연산자를 사용할 수 있다.
예를 들면 new (buffer) Widget (widgetSize);

메모리를 받아서 객체를 생성해주는 기능을 만들때 사용하면 된다.

void* operator(size_t size, void* location);

#include <new>를 추가해주면 사용 가능하다!

delete 연산자의 동작

  1. 객체의 소멸자 호출
  2. 메모리 제거

  • 메모리 지정 new를 이용해 생성된 객체를 delete로 지우지 말자.
    • 메모리가 어디서 할당되었는지 모르기 때문이다.
    • 메모리 지정 new를 이용했다면 메모리 생성, 해제하는 기능이 어딘가 있을테니
    • 소멸자 호출 후에 이를 이용해 메모리를 해제해주자.

배열의 할당 해제

opearator new[], delete[]는 오버로딩 가능하기는 하지만
쉽지 않기 때문에 사용하지 않는게 좋겠다 ㅋㅋㅋ

반응형
Posted by msparkms
,
반응형

항목1 - 포인터와 참조자를 구분하자.


C언어에서 열심히 사용되던 포인터와 C++에서 포인터를 대신하기 위한 레퍼런스.

비슷하지만 다른 특징을 가진 두 가지 개념을 구분해서 사용하는 것이 이번 정리의 목적이다.

 

구분을 위해서는 각각의 특징을 정리해보자.

 

레퍼런스

  1. 클래스나 구조체의 값을 접근할 때 '.'연산자를 이용한다.

  2. NULL 값을 저장할 수 없다. ( 메모리 공간을 차지하는 객체를 참조해야 한다. )

  3. 선언될 때 반드시 초기화해야 한다. ( 한번 참조하면 다른 객체를 참조 할 수 없다. )

 

포인터

  1. 클래스나 구조체의 값을 접근할 때 '->'연산자를 이용한다.

  2. NULL 값을 저장할 수 있다.

  3. 값을 참조하려면 * 연산자를 붙여야 한다.

 

특징을 통한 사용법

  • 만약 내가 가리킬 객체가 NULL이 될 경우가 있는 경우에는 무조건 포인터를 사용해야 한다.

  • NULL이 되지 않을 것이 보장된다면 레퍼런스를 사용해도 된다.

  • 반대로 생각하면 포인터의 경우에는 NULL 값인지 체크를 해야 한다.

  • opeator[]를 리턴할때 포인터면 *을 붙여야 값에 접근할 수 있으니 이럴때는 레퍼런스를 사용하는게 이쁘다.


책에서 NULL값을 가진 포인터를 레퍼런스가 받거나 하는 극단적인 예제들이 나와있는데

현업에서 코드가 복잡할 때 함수 리턴값이나 인자값 혹은 컨테이너들을 이용하다 보면 실수가 있을 수는 있을 것 같다.

만약 이런일이 발생한 다면 크래쉬가 나서 덤프를 확인하면서 힘들게 고치거나 안전하게 스마트 포인터를 이용하자!





항목2 - 가능한 C++ 스타일의 캐스트를 즐겨 쓰자.


C 스타일의 캐스트에 비해서 C++ 스타일의 캐스트는 기능별로 세부적으로 나누어져 있는 편이다.

그렇기 때문에 필요에 맞게 C++ 스타일의 캐스트를 이용하면 실수를 줄일 수 있고 코드를 볼 때 더 명확하게 의도를 파악할 수 있다.

C++ 스타일 캐스트를 이용하는것이 확실히 더 좋아보인다.


대신에 정말 간단한 캐스트이고 명확한 경우에는 기존 C 스타일 캐스트를 이용해도 될 것 같기는 하다.

그래도 가능하면 C++ 캐스트를 이용하자.


C++ 캐스트 간단 설명  

  • const_cast

    • 상수성 (const) / 휘발성 (volatile)을 붙이거나 제거할 때만 사용한다.

  • dynamic_cast

    • 파생 혹은 형제 클래스로 캐스팅할 때만 사용한다. 

    • 가능하지 않으면 NULL을 리턴함으로 NULL 체크가 필요하다.

    • 가상함수가 있는 경우에만 가능하다.

  • static_cast

    • 상수성 / 휘발성 / 상속관계가 아닌 경우에만 사용한다.

  • reinterpret_cast

    • 예제상으로는 함수 포인터 캐스팅 정도에 사용한다고 나와있다.

    • C 캐스팅과 비슷한 느낌이라고 보면 된다.

    • 컴파일러마다 결과가 다를 수 있어서 코드 이식에 문제가 있을 수도 있다고 한다.


항목3 - 배열과 다형성은 같은 수준으로 놓고 볼 것이 아니다.

엄청 중요한 내용은 아니라서 간단히 정리하면...

부모클래스와 자식클래스가 있다고 할때 C++에서는 다형성을 통해

부모클래스 포인터에 자식클래스의 주소를 받을 수 있고 오버라이딩해서 사용할 수 있다.

부모클래스 배열에 자식클래스 인스턴스를 넘겼다고 하자.

이때 직접적으로 배열의 인덱스를 통해 접근한다거나

new []로 부모클래스 배열을 만들고 자식클래스 인스턴스의 주소를 넣고 delete []를 한다거나

하는 직접적으로 주소에 접근할 경우에 배열은 부모클래스의 사이즈만큼 이동할 것이기 때문에 예기치 않은 동작을 할 거라는 내용;;



항목4 - 쓸데 없는 기본 생성자는 그냥 두지 말자.

여기서 말하는 기본 생성자는 아무런 인자를 받지 않고 호출될 수 있는 생성자를 말한다.

초기화 리스트등을 이용해서 디폴트 초기값을 넣어두는 용도로 보통 사용한다.

근데 만약에 디폴트값을 정의할 수 없고 인자를 받아야지만 초기화 할 수 있는 객체라면 인자를 받는 생성자를 만들어줘야 한다.

이렇게 되면 객체를 생성할 때 인자를 넣어줘야 하기 때문에 생성부분의 코드가 살짝 복잡해진다.

class EquipmentPiece {
public:
    EquipmentPiece(int IDNumber);
    ....
};

위와 같은 객체가 있다고 했을 때 배열을 생성하면 조금 귀찮아진다.

EquipmentPiece bestPiece[10]; // error
EquipmentPiece bestPiece[] = 
{
  EquipmentPiece(0),
  EquipmentPiece(1),
  ...
}; // OK

EquipmentPiece* bestPiece = new EquipmentPiece[10]; // error

typedef EquipmentPiece* PEP;
PEP bestPiece[10];
for (int i = 0; i < 10; ++i)
{
  bestPiece[i] = new EquipmentPiece(i); // OK
}

포인터를 사용하는 것이 깔끔해 보이기는 하는데 포인터를 동적할당해야 해서 메모리를 더 쓰는 문제가 있다.

이를 막기 위해서 그냥 10개짜리 메모리를 할당한다음에 placement new 연산자를 이용해서 미리 잡아놓은 메모리에 객체를 생성하는 방법을 사용해도 된다.

이럴 경우에는 소멸자도 따로 호출해줘야 하고 메모리 해제도 해야되고 마찬가지로 귀찮아진다.

자세한건 다른 항목에 나오니 일단 패스...


다른 문제점은 템플릿으로 구현된 컨테이너의 경우에 기본 생성자만 지원되는 경우가 있을 수 있어서 이런 경우에도 사용하기 힘들다.


다른 생각해볼 점은 가상 기본 클래스를 만들 때 기본 생성자를 지원하지 않는 경우이다.

이럴 경우 상속받는 쪽에서 생성자를 제대로 이해하고 구현해줘야 하는 문제가 있다.

상속받아서 사용하는것 치고는 불편한 느낌이라 잘 설계되었다고 말할 수 없을 것이다.

class EquipmentPiece {
public:
    EquipmentPiece(int IDNumber = UNSPECIFIED);
    ....
private:
    static const int UNSPECIFIED;
};

신선하게 위와 같이 만들어 기본 생성자인척 하게 끔 만들었다고 해보자.

인자를 안 넣어도 UNSPECIFIED가 들어가 지기 때문에 위에 문제들은 모두 해결된다.

하지만 생성만 편해진거고 나머지 구현에서는 IDNumber가 UNSPECIFIED인가를 검사하는 부분들이 들어갈 것이기 때문에

전체적으로 보면 오히려 더 안 좋은 코드가 될 것이다.


그때 그때 다르겠지만...

생성자에서 인자를 받아야만 한다면 그냥 받는 쪽으로 구현하자 ㅠㅠ

반응형
Posted by msparkms
,
반응형

warning MSB8004: Output Directory does not end with a trailing slash. 란 경고가 계속 뜨길래 자세히 봤더니

말그대로 Output Directory를 설정할 때 마지막 부분에  '\'를 빼먹었다는 경고였다.

경고는 보기 싫으니 바로 수정하였다. -0-;;;
반응형
Posted by msparkms
,
반응형

프로젝트에서 간단하게 stats을 표시하려고 하는데 printf 형식을 따라서 표시하기로 정했다.

그에 따라서 stats용으로 printf를 간단하게 구현하기로 했다.

이미 제공되어 있는 것들만 이용하면 쉽게 만들 수 있다.

void  CMSStats::StatsPrintf(DWORD index, TCHAR* fmt, ...)
{
 va_list marker;                                                               -> 가변인자를 처리할 va_list를 생성한다. 
 va_start(marker, fmt);                                                      -> 변환명세를 va_list에 저장한다.
 _vstprintf_s(tempBuffer_, MAX_STATS_BUFFER_SIZE, fmt, marker); -> tempBuffer_에 저장한다.
                                                                                    _s 버전은 두번째 인자에 사이즈를 넣는다.
AddStats(index, tempBuffer_);                                            -> Stats에 추가해준다.
}

반응형
Posted by msparkms
,