런타임 타입 정보
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
런타임 타입 정보(RTTI)는 C++, Object Pascal, Ada 등의 프로그래밍 언어에서 런타임에 객체의 타입을 확인하고 조작하는 기능이다. C++에서는 `dynamic_cast<>` 연산자와 `typeid` 연산자를 통해, Object Pascal과 Delphi에서는 `is`와 `as` 연산자를 통해 구현된다. RTTI는 다형성을 지원하는 클래스에서 안전한 형 변환과 타입 조작을 가능하게 하며, 객체의 클래스, 조상, 속성, 메서드 등에 대한 정보를 제공한다.
더 읽어볼만한 페이지
- C++ - 헤더 파일
헤더 파일은 프로그래밍 언어에서 코드 재사용성, 모듈화, 컴파일 시간 단축에 기여하며 함수 프로토타입, 변수 선언 등을 포함하고 `#include` 지시어로 소스 코드에 포함되어 사용되는 파일이다. - C++ - 소멸자 (컴퓨터 프로그래밍)
소멸자는 객체가 메모리에서 제거되기 직전에 호출되는 멤버 함수로, 객체 자원 해제 및 정리 작업을 수행하며, C++ 등 여러 언어에서 구현되고 메모리 누수 방지에 기여한다. - 자료형 - 참조
참조는 프로그래밍에서 메모리 주소나 다른 데이터를 가리키는 값으로, 데이터의 효율적인 전달과 공유를 위해 사용되며, 포인터, 파일 핸들, URL 등이 그 예시이다. - 자료형 - 익명 함수
익명 함수는 이름이 없는 함수로, 람다 추상, 람다 함수, 람다 표현식, 화살표 함수 등으로 불리며, 함수형 프로그래밍 언어에서 람다식 형태로 많이 사용되고 고차 함수의 인수, 클로저, 커링 등에 활용되지만, 재귀 호출의 어려움이나 기능 제한과 같은 단점도 존재한다.
| 런타임 타입 정보 |
|---|
2. C++
C++에서 런타임 타입 정보(RTTI)는 `dynamic_cast<>` 연산자와 `typeid` 연산자를 통해 제공된다.[12] RTTI는 런타임에 안전한 형 변환과 타입 조작을 가능하게 한다. RTTI는 다형적인 클래스, 즉 최소 하나의 가상 함수를 가진 클래스에서만 사용 가능하다. 이는 기본 클래스가 가상 소멸자를 가져야 하므로 자연스러운 제약이다.
일부 컴파일러에서는 RTTI를 선택적으로 비활성화할 수 있다. RTTI를 사용하지 않더라도 활성화에 따른 리소스 비용이 발생할 수 있다. `typeid`는 클래스 정보만 필요한 상황에서 `dynamic_cast` 보다 선호되기도 하는데, `typeid`가 상수 시간에 결과를 반환하는 반면 `dynamic_cast`는 런타임에 클래스 상속 구조를 탐색해야 할 수 있기 때문이다.
2. 1. `typeid` 연산자
`typeid` 키워드는 런타임에 객체의 타입을 결정하는 데 사용된다. `typeid`는 `std::type_info` 객체에 대한 참조를 반환하며, 이 객체는 프로그램 종료 시까지 존재한다.[12]`dynamic_cast`가 런타임에 클래스 상속 구조를 탐색해야 하는 반면, `typeid`는 상수 시간에 타입을 결정할 수 있어 효율적이다. (단, `std::type_info::name()`과 같은 일부 멤버 함수는 구현에 따라 다를 수 있다.)
널 포인터에 `typeid` 연산자를 적용할 때 `unary *` 연산자를 사용하면 `std::bad_typeid` 예외가 발생한다. 즉, `p`가 널 포인터일 때, 예외를 발생시키려면 `typeid(*p)` 형태로 사용해야 한다.
2. 1. 1. `typeid` 예제
cpp#include
#include
class Person {
public:
virtual ~Person() = default;
};
class Employee : public Person {};
int main() {
Person person;
Employee employee;
Person* ptr = &employee;
Person& ref = employee;
// typeid::name이 반환하는 문자열은 구현에 따라 다릅니다.
std::cout << typeid(person).name()
<< std::endl; // Person (컴파일 시간에 정적으로 알려져 있음).
std::cout << typeid(employee).name()
<< std::endl; // Employee (컴파일 시간에 정적으로 알려져 있음).
std::cout << typeid(ptr).name()
<< std::endl; // Person* (컴파일 시간에 정적으로 알려져 있음).
std::cout << typeid(*ptr).name()
<< std::endl; // Employee (런타임에 동적으로 조회됨
// 다형성 클래스에 대한 포인터의 역참조이기 때문).
std::cout << typeid(ref).name()
<< std::endl; // Employee (참조도 다형성을 가질 수 있음)
Person* p = nullptr;
try {
typeid(*p); // 정의되지 않은 동작이 아님; std::bad_typeid를 던짐.
} catch (...) {
}
Person& p_ref = *p; // 정의되지 않은 동작: 널 역참조
typeid(p_ref); // std::bad_typeid를 던지기 위한 요구 사항을 충족하지 않음
// typeid에 대한 표현식이 결과가 아니기 때문
// 단항 * 연산자를 적용합니다.
}
```
출력 (정확한 출력은 시스템 및 컴파일러에 따라 다름):
```text
Person
Employee
Person*
Employee
Employee
```
`typeid` 키워드는 런타임 시 클래스의 객체를 결정하는 데 사용된다. `std::type_info` 객체에 대한 참조를 반환하며, 이는 프로그램 종료 시까지 존재한다.[8]
2. 2. `dynamic_cast` 연산자
`dynamic_cast` 연산자는 C++에서 클래스 계층 구조 내에서 포인터나 참조를 더 구체적인 타입으로 변환(다운캐스팅)하는 데 사용된다.[13] `static_cast`와 달리 `dynamic_cast`는 런타임에 타입 안전성을 검사한다. 타입이 호환되지 않으면, 포인터를 다룰 때는 널 포인터를 반환하고, 참조를 다룰 때는 `std::bad_cast` 예외를 발생시킨다.[13]`dynamic_cast`는 주로 다형성 클래스, 즉 적어도 하나의 가상 함수를 가진 클래스에 사용된다. 이는 런타임 타입 정보(RTTI)가 가상 함수 테이블에 포함되기 때문이다.
`dynamic_cast`의 동작 방식은 Java의 타입 캐스팅과 유사하다. Java에서 타입 캐스팅 시 객체가 대상 타입의 인스턴스가 아니거나 변환될 수 없으면 `java.lang.ClassCastException` 예외가 발생한다.[13]
어떤 함수가 `A` 타입의 객체를 인자로 받고, 전달받은 객체가 `A`의 서브클래스인 `B`의 인스턴스인 경우, 추가적인 연산을 수행하려면 `dynamic_cast`를 사용한다.
2. 2. 1. `dynamic_cast` 예제
cpp#include
#include
#include
#include
using namespace std;
class A {
public:
// RTTI는 가상 메서드 테이블에 포함되어 있으므로, 적어도 하나의 가상 함수가 있어야 합니다.
virtual ~A() = default;
void MethodSpecificToA() {
cout << "A에 특화된 메서드가 호출되었습니다" << endl;
}
};
class B : public A {
public:
void MethodSpecificToB() {
cout << "B에 특화된 메서드가 호출되었습니다" << endl;
}
};
void MyFunction(A& my_a) {
try {
// 캐스팅은 B 타입 객체에 대해서만 성공합니다.
B& my_b = dynamic_cast(my_a);
my_b.MethodSpecificToB();
} catch (const bad_cast& e) {
cerr << " 예외 " << e.what() << "가 발생했습니다." << endl;
cerr << " 객체는 B 타입이 아닙니다." << endl;
}
}
int main() {
array
array_of_a[0] = make_unique(); // B 객체에 대한 포인터.
array_of_a[1] = make_unique(); // B 객체에 대한 포인터.
array_of_a[2] = make_unique(); // A 객체에 대한 포인터.
for (int i = 0; i < 3; ++i)
MyFunction(*array_of_a[i]);
}
```
콘솔 출력:
```text
B에 특화된 메서드가 호출되었습니다
B에 특화된 메서드가 호출되었습니다
예외 std::bad_cast가 발생했습니다.
객체는 B 타입이 아닙니다
```
`MyFunction`과 유사한 버전은 참조 대신 포인터를 사용하여 작성할 수 있습니다.
```cpp
void MyFunction(A* my_a) {
B* my_b = dynamic_cast(my_a);
if (my_b != nullptr)
my_b->methodSpecificToB();
else
std::cerr << " 객체는 B 타입이 아닙니다" << std::endl;
}
3. Object Pascal, Delphi
Object Pascal과 델파이에서 런타임 타입 정보(RTTI)는 `as` 연산자를 사용하여 안전한 타입 캐스팅을 수행하고, `is` 연산자를 사용하여 객체가 속한 클래스를 테스트한다.[1] 또한, `RTTI` 유닛을 통해 런타임에 타입 정보를 조작할 수 있다.[1]
3. 1. `is` 연산자
Object Pascal과 델파이에서 `is` 연산자는 런타임에 객체의 클래스 타입을 확인하는 데 사용된다. 이 연산자는 객체가 주어진 클래스에 속하는지, 상속 계층 구조에 있는 조상 클래스를 포함하여 테스트한다. 예를 들어 `Button1`은 `TButton` 클래스이며, `TWinControl` → `TControl` → `TComponent` → `TPersistent` → `TObject`의 조상을 가진다. 여기서 `TObject`는 모든 클래스의 조상이다.[1]3. 2. `as` 연산자
델파이를 포함한 Object Pascal에서 `as` 연산자는 런타임에 객체를 조상 클래스에 속하는 것처럼 처리할 때 사용된다.[1]3. 3. `RTTI` 유닛
RTTI 유닛은 런타임에 객체 타입 정보를 조작하는 데 사용된다. 이 유닛은 객체의 클래스 및 해당 조상, 속성, 메서드, 이벤트에 대한 정보를 얻고, 속성 값을 변경하고 메서드를 호출할 수 있는 일련의 클래스를 포함한다.[1]3. 3. 1. Object Pascal, Delphi 예제
Object Pascal과 델파이에서 연산자 `is`는 런타임에 클래스의 타입을 확인하는 데 사용된다. 이 연산자는 객체가 주어진 클래스에 속하는지, 상속 계층 구조 트리에 있는 개별 조상 클래스를 포함하여 테스트한다(예: ''Button1''은 ''TButton'' 클래스이며, ''TWinControl'' → ''TControl'' → ''TComponent'' → ''TPersistent'' → ''TObject''의 조상을 가지고 있으며, 여기서 ''TObject''는 모든 클래스의 조상이다). 연산자 `as`는 객체가 런타임에 조상 클래스에 속하는 것처럼 처리되어야 할 때 사용된다.RTTI 유닛은 런타임에 객체 타입 정보를 조작하는 데 사용된다. 이 유닛에는 객체의 클래스 및 해당 조상, 속성, 메서드 및 이벤트에 대한 정보를 얻고, 속성 값을 변경하고 메서드를 호출할 수 있는 일련의 클래스가 포함되어 있다. 다음 예제는 RTTI 모듈을 사용하여 객체가 속한 클래스에 대한 정보를 얻고, 이를 생성하고, 해당 메서드를 호출하는 것을 보여준다. 이 예제에서는 TSubject 클래스가 SubjectUnit이라는 유닛에 선언되었다고 가정한다.
```objectpascal
uses
RTTI, SubjectUnit;
procedure WithoutReflection;
var
MySubject: TSubject;
begin
MySubject := TSubject.Create;
try
Subject.Hello;
finally
Subject.Free;
end;
end;
procedure WithReflection;
var
RttiContext: TRttiContext;
RttiType: TRttiInstanceType;
Subject: TObject;
begin
RttiType := RttiContext.FindType('SubjectUnit.TSubject') as TRttiInstanceType;
Subject := RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsObject;
try
RttiType.GetMethod('Hello').Invoke(Subject, []);
finally
Subject.Free;
end;
end;
4. Ada
아다(Ada)에서 태그된 타입의 객체는 런타임에 이들 객체의 타입을 식별할 수 있는 타입 태그를 저장한다. `in` 연산자는 런타임에 객체가 특정 타입인지 테스트하고 안전하게 변환할 수 있는지 테스트하는 데 사용된다.[1]
참조
[1]
웹사이트
Runtime Type Identification
https://docs.oracle.[...]
Oracle
2015-04-16
[2]
웹사이트
Language support library [support.rtti]
https://eel.is/c++dr[...]
2021-07-13
[3]
웹사이트
Object-oriented programming
https://learn.adacor[...]
2021-07-13
[4]
웹사이트
A History of C++: 1979—1991
http://www.stroustru[...]
Bjarne Stroustrup
2009-05-18
[5]
웹사이트
Working with RTTI - RAD Studio
http://docwiki.embar[...]
2021-06-06
[6]
서적
Ada 95: The Craft of Object-Oriented Programming
https://www.adaic.or[...]
2021-07-13
[7]
웹사이트
Avoiding RTTI, and support for -fno-rtti in Arm Compiler 6
https://developer.ar[...]
2021-07-13
[8]
문서
C++ standard (ISO/IEC14882) section 5.2.8 [expr.typeid], 18.5.1 [lib.type.info]
http://cs.nyu.edu/co[...]
[9]
웹사이트
ClassCastException (Java Platform SE 8)
http://docs.oracle.c[...]
[10]
간행물
Sun Microsystems
2000
[11]
문서
비야네 스트롭스트룹
[12]
문서
C++ standard (ISO/IEC14882) section 5.2.8 [expr.typeid], 18.5.1 [lib.type.info]
http://cs.nyu.edu/co[...]
[13]
url
http://docs.oracle.c[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com