[C++] (번역)형변환(2) - dynamic/static/reinterpret/const

김형태·2021년 5월 20일
0

C++

목록 보기
13/13

1. Type casting (타입 캐스팅, 형변환)

C++ is a strong-typed language. Many conversions, specially those that imply a different interpretation of the value, require an explicit conversion, known in C++ as type-casting. There exist two main syntaxes for generic type-casting: functional and c-like:

C++는 강타입 언어이다(강타입 언어가 뭐지?. 많은 변환, 특히 값의 다른 해석을 암시하는 변환은 C++에서 타입 캐스팅으로 알려진 명시적 변환을 필요로 한다. 일반적인 타입 캐스팅에는 두 가지 주요 구문이 있다:
functional과 c-like:

double x = 10.3;
int y;
y = int (x);    // functional notation
y = (int) x;    // c-like cast notation

The functionality of these generic forms of type-casting is enough for most needs with fundamental data types. However, these operators can be applied indiscriminately on classes and pointers to classes, which can lead to code that -while being syntactically correct- can cause runtime errors. For example, the following code compiles without errors:

이러한 일반적인 형태의 타입 캐스팅은 기본 데이터 유형을 사용하는 대부분의 요구에 충분하다. 그러나 이러한 연산자는 클래스와 클래스 포인터에 무차별적으로 적용될 수 있으며, 이는 문법적으로는 올바르지만 런타임 오류를 발생시킬 수 있는 코드로 이어질 수 있다. 예를 들어 다음 코드는 오류 없이 컴파일된다.

// class type-casting
#include <iostream>
using namespace std;

class Dummy {
    double i,j;
};

class Addition {
    int x,y;
  public:
    Addition (int a, int b) { x=a; y=b; }
    int result() { return x+y;}
};

int main () {
  Dummy d;
  Addition * padd;
  padd = (Addition*) &d;
  cout << padd->result();
  return 0;
}

The program declares a pointer to Addition, but then it assigns to it a reference to an object of another unrelated type using explicit type-casting:

이 프로그램은 포인터를 Addition에 선언하지만 명시적 타입 캐스팅을 사용하여 관련 없는 다른 유형의 개체에 대한 참조를 할당합니다.

padd = (Addition*) &d;

Unrestricted explicit type-casting allows to convert any pointer into any other pointer type, independently of the types they point to. The subsequent call to member result will produce either a run-time error or some other unexpected results.

제한없는 명시적 타입 캐스팅은 포인터가 가리키는 유형과 무관하게 다른 포인터 유형으로 변환할 수 있다. 이후 멤버 결과를 호출하면 런타임 오류 또는 기타 예기치 않은 결과가 발생한다.

In order to control these types of conversions between classes, we have four specific casting operators: dynamic_cast, reinterpret_cast, static_cast and const_cast. Their format is to follow the new type enclosed between angle-brackets (<>) and immediately after, the expression to be converted between parentheses.

클래스 간 이러한 변환 유형을 제어하기 위해, 네 가지 특정 캐스트 연산자가 있다: dynamic_cast, reinterpret_cast, static_cast and const_cast. 각 괄호 사이에 둘러싸인 새로운 타입(< >)과 괄호 사이에 변환될 표현식을 즉시 따르는 형식이다.

dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)

The traditional type-casting equivalents to these expressions would be:

이러한 표현에 해당하는 기존의 타입 캐스팅은 다음과 같다.

(new_type) expression
new_type (expression)

but each one with its own special characteristics:

각각의 특별한 특징이 있는 것은 다음과 같습니다:


2. dynamic_cast

dynamic_cast can only be used with pointers and references to classes (or with void*). Its purpose is to ensure that the result of the type conversion points to a valid complete object of the destination pointer type.

dynamic_cast는 클래스에 대한 포인터 및 참조(또는 void*)에만 사용할 수 있습니다. 이 방법의 목적은 형변환의 결과가 대상 포인터 유형의 유효한 전체 객체를 가리키는지 확인하는 것입니다.

This naturally includes pointer upcast (converting from pointer-to-derived to pointer-to-base), in the same way as allowed as an implicit conversion.

여기에는 묵시적 변환과 동일한 방식으로 포인터 업캐스트(pointer-to-derived에서 pointer-to-base로)가 포함된다.

But dynamic_cast can also downcast (convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with virtual members) if -and only if- the pointed object is a valid complete object of the target type. For example:

그러나 dynamic_cast는 또한 포인트된 객체가 대상 유형의 유효한 완전한 객체인 경우에만 다형 클래스(가상 멤버를 가진 클래스)를 다운캐스트(pointer-to-base에서 pointer-to-derived로)할 수 있다. 예를 들어:

// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;

class Base { virtual void dummy() {} };
class Derived: public Base { int a; };

int main () {
  try {
    Base * pba = new Derived;
    Base * pbb = new Base;
    Derived * pd;

    pd = dynamic_cast<Derived*>(pba);
    if (pd==0) cout << "Null pointer on first type-cast.\n";

    pd = dynamic_cast<Derived*>(pbb);
    if (pd==0) cout << "Null pointer on second type-cast.\n";

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}

Compatibility note: This type of dynamic_cast requires Run-Time Type Information (RTTI) to keep track of dynamic types. Some compilers support this feature as an option which is disabled by default. This needs to be enabled for runtime type checking using dynamic_cast to work properly with these types.

호환성 노트: 이 유형의 dynamic_cast를 사용하려면 RTTI(Run-Time Type Information)가 필요하다. 일부 컴파일러는 기본적으로 비활성화된 옵션으로 이 기능을 지원합니다. 이러한 유형에서 올바르게 작동하려면 dynamic_cast를 사용하여 런타임 유형을 확인하려면 이 옵션을 활성화해야 한다.

The code above tries to perform two dynamic casts from pointer objects of type Base (pba and pbb) to a pointer object of type Derived, but only the first one is successful. Notice their respective initializations:

위의 코드는 Base *(pba 및 pbb) 유형의 포인터 객체에서 Derived * 유형의 포인터 개체로 두 개의 dynamic cast를 수행하려고 시도하지만, 첫 번째만 성공적이다. 각각의 초기화에 주목하자.

Base * pba = new Derived;
// Base * 로 실제로는 Derived(Base를 상속한)를 가리킴
Base * pbb = new Base;
// Base * 로 포인터 유형에 맞는 클래스를 가리킴

두 가지 모두 Base* 유형의 포인터임에도 불구하고 pba는 실제로는 Derived 유형의 객체를 가리키고 pbb는 Base 유형의 객체를 가리킨다. 그러므로, 각각의 타입캐스트가 dynamic_cast를 사용하여 수행될 때, pba는 derived 클래스의 전체 객체를 가리키는 반면, pbb는 Derived 클래스의 불완전한 객체인 Base 클래스의 객체를 가리킨다.

When dynamic_cast cannot cast a pointer because it is not a complete object of the required class -as in the second conversion in the previous example- it returns a null pointer to indicate the failure. If dynamic_cast is used to convert to a reference type and the conversion is not possible, an exception of type bad_cast is thrown instead.

dynamic_cast가 필요한 클래스의 완전한 객체가 아니기 때문에 포인터를 캐스팅할 수 없는 경우(이전 예에서 두 번째 변환) null 포인터를 반환하여 오류를 나타낸다. dynamic_cast를 사용하여 참조자 유형으로 변환하는 데 사용되고, 변환이 불가능한 경우 bad_cast 유형의 예외가 발생한다.

dynamic_cast can also perform the other implicit casts allowed on pointers: casting null pointers between pointers types (even between unrelated classes), and casting any pointer of any type to a void* pointer.

dynamic_cast는 포인터에 허용된 다른 묵시적 캐스트도 수행할 수 있다: 포인터 유형 사이에 null 포인터를 캐스팅하는 것(관련되지 않은 클래스 사이에도 포함), 그리고 어떤 유형의 포인터에서 void *포인터로 캐스팅하는 것.
(????이건 무슨 말인지 아직 모르겠땅)


3. static_cast

static_cast can perform conversions between pointers to related classes, not only upcasts (from pointer-to-derived to pointer-to-base), but also downcasts (from pointer-to-base to pointer-to-derived). No checks are performed during runtime to guarantee that the object being converted is in fact a full object of the destination type. Therefore, it is up to the programmer to ensure that the conversion is safe. On the other side, it does not incur the overhead of the type-safety checks of dynamic_cast.

static_cast는 관련 클래스의 포인터 간 변환을 수행할 수 있다. 업캐스트(pointer-to-derived 대 pointer-to-base)뿐만 아니라 다운캐스트(pointer-to-base에서 pointer-to-derived)도 수행할 수 있다. 런타임 중에는 변환 중인 객체가 사실상 대상 유형의 전체 객체인지 확인하기 위해 검사가 수행되지 않는다. 따라서 변환이 안전한지 확인하는 것은 프로그래머에게 달려 있다. 반면, dynamic_cast의 형식 안전 점검에 대한 오버헤드(오버헤드가 뭐지?)가 발생하지 않습니다.

class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);

This would be valid code, although b would point to an incomplete object of the class and could lead to runtime errors if dereferenced.

b가 클래스의 불완전한 객체를 가리키며 참조가 해제되면 런타임 오류가 발생할 수 있지만 이것은 유효한 코드일 것이다.

Therefore, static_cast is able to perform with pointers to classes not only the conversions allowed implicitly, but also their opposite conversions.

따라서 static_cast는 클래스에 대한 포인터에도 수행될 수 있다. 묵시적으로 허용되는 변환뿐만 아니라 반대로의 변환에 대해서도 수행할 수 있다.

static_cast is also able to perform all conversions allowed implicitly (not only those with pointers to classes), and is also able to perform the opposite of these. It can:

  • Convert from void to any pointer type. In this case, it guarantees that if the void value was obtained by converting from that same pointer type, the resulting pointer value is the same.
  • Convert integers, floating-point values and enum types to enum types.

static_cast는 또한 묵시적으로 허용되는 모든 변환을 수행할 수 있으며(클래스에 대한 포인터가 있는 변환뿐만 아니라), 이러한 변환의 반대도 수행할 수 있다. 다음을 수행할 수 있다.

  • void*에서 모든 포인터 유형으로 변환한다. 이 경우, 동일한 포인터 유형에서 변환하여 void* 값을 얻은 경우 결과 포인터 값이 동일함을 보증한다.
  • 정수, 부동 소수점 값 및 열거형을 열거형으로 변환한다.

Additionally, static_cast can also perform the following:

  • Explicitly call a single-argument constructor or a conversion operator.
  • Convert to rvalue references.
  • Convert enum class values into integers or floating-point values.
  • Convert any type to void, evaluating and discarding the value.

추가적으로 static_cast는 아래를 수행할 수 있다.

  • 단일 인수 생성자 또는 변환 연산자를 명시적으로 호출한다.
  • rvalue 참조를 변환한다.
  • 열거 클래스 값을 정수 또는 부동 소수점 값으로 변환한다.
  • 값을 평가 및 폐기하면서 모든 유형을 void로 변환한다.

4. reinterpret_cast

reinterpret_cast converts any pointer type to any other pointer type, even of unrelated classes. The operation result is a simple binary copy of the value from one pointer to the other. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked.

removal_cast는 모든 포인터 유형을 관련 없는 클래스라도 다른 포인터 유형으로 변환한다. 작업 결과는 한 포인터의 값을 다른 포인터로 단순 바이너리 복사한 것이다. 모든 포인터 변환이 허용된다. 포인터된 콘텐츠나 포인터 유형 자체는 선택되지 않는다.

It can also cast pointers to or from integer types. The format in which this integer value represents a pointer is platform-specific. The only guarantee is that a pointer cast to an integer type large enough to fully contain it (such as intptr_t), is guaranteed to be able to be cast back to a valid pointer.

정수 형식에서(또는 정수 형식으로) 포인터를 캐스트할 수도 있다. 이 정수 값이 포인터를 나타내는 형식은 플랫폼마다 다르다. 유일한 보장은 완전히 포함할 수 있을 만큼 큰 정수형(예: intptr_t)에 캐스트된 포인터가 유효한 포인터로 다시 캐스트될 수 있다는 것이다.

The conversions that can be performed by reinterpret_cast but not by static_cast are low-level operations based on reinterpreting the binary representations of the types, which on most cases results in code which is system-specific, and thus non-portable. For example:

reinterpret_cast로 수행 할 수 있지만 static_cast로는 수행 할 수 없는 변환은 유형의 이진 표현을 재 해석하는 것을 기반으로하는 저수준 작업이며, 대부분의 경우 코드는 시스템에 따라 다르므로 이식 할 수 없다. 예를 들면 :

class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);

This code compiles, although it does not make much sense, since now b points to an object of a totally unrelated and likely incompatible class. Dereferencing b is unsafe.

이 코드는 별 의미가 없지만 컴파일된다. 이제 b는 완전히 관련이 없고 호환되지 않을 가능성이 있는 클래스의 객체를 가리키기 때문이다. b를 역참조하는 것은 안전하지 않다.


5. const_cast

This type of casting manipulates the constness of the object pointed by a pointer, either to be set or to be removed. For example, in order to pass a const pointer to a function that expects a non-const argument:

이 유형의 캐스팅은 포인터가 가리키는 물체의 constness를 설정 또는 제거하도록 조작한다. 예를 들어, const가 아닌 인수를 예상하는 함수에 const 포인터를 전달하려면 :

// const_cast
#include <iostream>
using namespace std;

void print (char * str)
{
  cout << str << '\n';
}

int main () {
  const char * c = "sample text";
  print ( const_cast<char *> (c) );
  return 0;
}

The example above is guaranteed to work because function print does not write to the pointed object. Note though, that removing the constness of a pointed object to actually write to it causes undefined behavior.

위의 예는 print 함수가 포인트된 객체에 쓰이지 않기 때문에 효과가 보장된다. 그러나 실제로 쓰기 위해 포인트된 객체의 constness를 제거하면 정의되지 않은 동작이 발생한다.(????)


profile
steady

0개의 댓글