Перейти к содержимому

Как узнать класс объекта с

  • автор:

Object. Get Type Метод

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Возвращает объект Type для текущего экземпляра.

public: Type ^ GetType();
public Type GetType ();
member this.GetType : unit -> Type
Public Function GetType () As Type
Возвращаемое значение

Точный тип текущего экземпляра в среде выполнения.

Примеры

В следующем примере кода показано, что GetType возвращает тип среды выполнения текущего экземпляра.

using namespace System; public ref class MyBaseClass <>; public ref class MyDerivedClass: MyBaseClass<>; int main() < MyBaseClass^ myBase = gcnew MyBaseClass; MyDerivedClass^ myDerived = gcnew MyDerivedClass; Object^ o = myDerived; MyBaseClass^ b = myDerived; Console::WriteLine( "mybase: Type is ", myBase->GetType() ); Console::WriteLine( "myDerived: Type is ", myDerived->GetType() ); Console::WriteLine( "object o = myDerived: Type is ", o->GetType() ); Console::WriteLine( "MyBaseClass b = myDerived: Type is ", b->GetType() ); > /* This code produces the following output. mybase: Type is MyBaseClass myDerived: Type is MyDerivedClass object o = myDerived: Type is MyDerivedClass MyBaseClass b = myDerived: Type is MyDerivedClass */ 
using System; public class MyBaseClass < >public class MyDerivedClass: MyBaseClass < >public class Test < public static void Main() < MyBaseClass myBase = new MyBaseClass(); MyDerivedClass myDerived = new MyDerivedClass(); object o = myDerived; MyBaseClass b = myDerived; Console.WriteLine("mybase: Type is ", myBase.GetType()); Console.WriteLine("myDerived: Type is ", myDerived.GetType()); Console.WriteLine("object o = myDerived: Type is ", o.GetType()); Console.WriteLine("MyBaseClass b = myDerived: Type is ", b.GetType()); > > // The example displays the following output: // mybase: Type is MyBaseClass // myDerived: Type is MyDerivedClass // object o = myDerived: Type is MyDerivedClass // MyBaseClass b = myDerived: Type is MyDerivedClass 
type MyBaseClass() = class end type MyDerivedClass() = inherit MyBaseClass() let myBase = MyBaseClass() let myDerived = MyDerivedClass() let o: obj = myDerived let b: MyBaseClass = myDerived printfn $"mybase: Type is " printfn $"myDerived: Type is " printfn $"object o = myDerived: Type is " printfn $"MyBaseClass b = myDerived: Type is " // The example displays the following output: // mybase: Type is MyBaseClass // myDerived: Type is MyDerivedClass // object o = myDerived: Type is MyDerivedClass // MyBaseClass b = myDerived: Type is MyDerivedClass 
' Define a base and a derived class. Public Class MyBaseClass End Class Public Class MyDerivedClass : Inherits MyBaseClass End Class Public Class Test Public Shared Sub Main() Dim base As New MyBaseClass() Dim derived As New MyDerivedClass() Dim o As Object = derived Dim b As MyBaseClass = derived Console.WriteLine("base.GetType returns ", base.GetType()) Console.WriteLine("derived.GetType returns ", derived.GetType()) Console.WriteLine("Dim o As Object = derived; o.GetType returns ", o.GetType()) Console.WriteLine("Dim b As MyBaseClass = derived; b.Type returns ", b.GetType()) End Sub End Class ' The example displays the following output: ' base.GetType returns MyBaseClass ' derived.GetType returns MyDerivedClass ' Dim o As Object = derived; o.GetType returns MyDerivedClass ' Dim b As MyBaseClass = derived; b.Type returns MyDerivedClass 

Комментарии

Так как System.Object является базовым классом для всех типов в системе GetType типов .NET, метод можно использовать для возврата Type объектов, представляющих все типы .NET. .NET распознает следующие пять категорий типов:

  • Классы, производные от System.Object,
  • Типы значений, производные от System.ValueType.
  • Интерфейсы, производные от System.Object начиная с платформа .NET Framework 2.0.
  • Перечисления, производные от System.Enum.
  • Делегаты, производные от System.MulticastDelegate.

Для двух объектов x , y имеющих одинаковые типы среды выполнения, Object.ReferenceEquals(x.GetType(),y.GetType()) возвращает . true В следующем примере используется GetType метод с методом , ReferenceEquals чтобы определить, относится ли одно числовое значение к тому же типу, что и два других числовых значения.

int n1 = 12; int n2 = 82; long n3 = 12; Console.WriteLine("n1 and n2 are the same type: ", Object.ReferenceEquals(n1.GetType(), n2.GetType())); Console.WriteLine("n1 and n3 are the same type: ", Object.ReferenceEquals(n1.GetType(), n3.GetType())); // The example displays the following output: // n1 and n2 are the same type: True // n1 and n3 are the same type: False 
open System let n1 = 12 let n2 = 82 let n3 = 12L printfn $"n1 and n2 are the same type: " printfn $"n1 and n3 are the same type: " // The example displays the following output: // n1 and n2 are the same type: True // n1 and n3 are the same type: False 
Module Example Public Sub Main() Dim n1 As Integer = 12 Dim n2 As Integer = 82 Dim n3 As Long = 12 Console.WriteLine("n1 and n2 are the same type: ", Object.ReferenceEquals(n1.GetType(), n2.GetType())) Console.WriteLine("n1 and n3 are the same type: ", Object.ReferenceEquals(n1.GetType(), n3.GetType())) End Sub End Module ' The example displays the following output: ' n1 and n2 are the same type: True ' n1 and n3 are the same type: False 

Чтобы определить, является ли объект определенным типом, можно использовать ключевое слово или конструкцию сравнения типов языка. Например, можно использовать конструкцию TypeOf…Is в Visual Basic или ключевое is слово в C#.

Метод GetType наследуется всеми типами, производными от Object. Это означает, что в дополнение к использованию ключевого слова сравнения собственного языка можно использовать GetType метод для определения типа конкретного объекта, как показано в следующем примере.

object[] values = < (int) 12, (long) 10653, (byte) 12, (sbyte) -5, 16.3, "string" >; foreach (var value in values) < Type t = value.GetType(); if (t.Equals(typeof(byte))) Console.WriteLine("is an unsigned byte.", value); else if (t.Equals(typeof(sbyte))) Console.WriteLine(" is a signed byte.", value); else if (t.Equals(typeof(int))) Console.WriteLine(" is a 32-bit integer.", value); else if (t.Equals(typeof(long))) Console.WriteLine(" is a 64-bit integer.", value); else if (t.Equals(typeof(double))) Console.WriteLine(" is a double-precision floating point.", value); else Console.WriteLine("'' is another data type.", value); > // The example displays the following output: // 12 is a 32-bit integer. // 10653 is a 32-bit integer. // 12 is an unsigned byte. // -5 is a signed byte. // 16.3 is a double-precision floating point. // 'string' is another data type. 
let values: obj[] = [| 12; 10653L; 12uy -5y; 16.3; "string" |] for value in values do let t = value.GetType() if t.Equals typeof then printfn $" is an unsigned byte." elif t.Equals typeof then printfn $" is a signed byte." elif t.Equals typeof then printfn $" is a 32-bit integer." elif t.Equals typeof then printfn $" is a 64-bit integer." elif t.Equals typeof then printfn $" is a double-precision floating point." else printfn $"'' is another data type." // The example displays the following output: // 12 is a 32-bit integer. // 10653 is a 32-bit integer. // 12 is an unsigned byte. // -5 is a signed byte. // 16.3 is a double-precision floating point. // 'string' is another data type. 
Module Example Public Sub Main() Dim values() As Object = < 12, CLng(10653), CByte(12), CSbyte(-5), 16.3, "string" >For Each value In values Dim t AS Type = value.GetType() If t.Equals(GetType(Byte)) Console.WriteLine(" is an unsigned byte.", value) ElseIf t.Equals(GetType(SByte)) Console.WriteLine(" is a signed byte.", value) ElseIf t.Equals(GetType(Integer)) Console.WriteLine(" is a 32-bit integer.", value) ElseIf t.Equals(GetType(Long)) Console.WriteLine(" is a 64-bit integer.", value) ElseIf t.Equals(GetType(Double)) Console.WriteLine(" is a double-precision floating point.", value) Else Console.WriteLine("'' is another data type.", value) End If Next End Sub End Module ' The example displays the following output: ' 12 is a 32-bit integer. ' 10653 is a 32-bit integer. ' 12 is an unsigned byte. ' -5 is a signed byte. ' 16.3 is a double-precision floating point. ' 'string' is another data type. 

Объект Type предоставляет метаданные, связанные с классом текущего Objectобъекта .

Определение класса в языке C++ и создание его объекта

Класс — это производный структурированный тип, введенный программистом на основе уже существующих типов. Другими словами, механизм классов задает некоторую структурированную совокупность типизированных данных и позволяет определить набор операций над этими данными.

Общий синтаксис класса можно определить с помощью конструкции:

class имя_класса { список_компонентов };

имя_класса — произвольно выбираемый идентификатор

список_компонентов — определения и описания типизированных данных и принадлежащих классу функций. Компонентами класса могут быть данные, функции, классы, перечисления, битовые поля и имена типов. Вначале для простоты будем считать, что компоненты класса — это типизированные данные (базовые и производные) и функции.

Заключенный в фигурные скобки список компонентов называют телом класса.
Телу класса предшествует заголовок. В простейшем случае заголовок класса включает слово class и имя.
Определение класса всегда заканчивается точкой с запятой.

Итак, принадлежащие классу функции мы будем называть методами класса или компонентными функциями. Данные класса — компонентными данными или элементами данных класса.

Вернемся к определению: класс — это тип, введенный программистом. А, так как, каждый тип служит для определения объектов, определим синтаксис создания объекта класса.

имя_класса имя_объекта;

Определение объекта класса предусматривает выделение участка памяти и деление этого участка на фрагменты, соответствующие отдельным элементам объекта.

Способы доступа к компонентам класса.

Существует несколько уровней доступа к компонентам класса. Рассмотрим основные:

public — члены класса открыты для доступа извне.
private — члены класса закрыты для доступа извне.

По умолчанию все переменные и функции, принадлежащие классу, определены как закрытые (private). Это означает, что они могут использоваться только внутри функций-членов самого класса. Для других частей программы, таких как функция main(), доступ к закрытым членам запрещен. Это, кстати, единственное отличие класса от структуры — в структуре все члены по умолчанию — public.

С использованием спецификатора доступа public можно создать открытый член класса, доступный для использования всеми функциями программы (как внутри класса, так и за его пределами).

class имя_класса { закрытые переменный и функции; защищенные члены данных; защищенные конструкторы; защищенные методы; public: открытые переменные и функции; общедоступные свойства; общедоступные члены данных; общедоступные конструкторы; общедоступный деструктор; общедоступные методы; } список имен объектов;

Синтаксис для доступа к данным конкретного объекта заданного класса (как и в случае структур), таков:

имя_объекта.имя_члена класса;

Пришло время примера…

# include using namespace std; class Test{ // так как спецификатор доступа не указан // данная переменная будет по умолчанию закрыта // для доступа вне класса (private) int one; // спецификатор доступа public // все члены, идущие после него // будут открыты для доступа извне public: // инициализировать переменные в классе // при создании запрещено, поэтому мы определяем // метод, реализующий данное действие void Initial(int o,int t){ one=o; two=t; } // метод показывающий переменные класса // на экран void Show(){ cout"\n\n"one"\t"two"\n\n"; } int two; }; void main(){ // создается объект с типом Test Test obj; // вызывается функция, инициализирующая его свойства obj.Initial(2,5); // показ на экран obj.Show(); // 2 5 // прямая запись в открытую переменную two // с переменной one такая запись невозможна, так // как доступ к ней закрыт obj.two=45; // снова показ на экран obj.Show(); // 2 45 }

Вышеописанный пример вполне интуитивно прост, однако, выделим один из главных моментов — переменные класса нет необходимости передавать в методы класса в качестве параметров, так как они видны в них автоматически.

Оператор .

Внутри класса функции-члены могут вызывать друг друга и обращаться к к переменным членам, не используя оператор точка(.) Этот оператор необходим, лишь когда обращение к функциям и переменным-членам осуществляется извне класса.

Оператор ->

Указатели на объекты. Доступ к членам объекта можно осуществлять и через указатель на объект. В этом случае применяется операция стрелка (→).

Оператор ::

Оператор :: называется оператором разрешения области видимости (scope resolution operator). Т.е. создается шаблон класса и в нем шаблоны методов класса, а сами методы описываются вне описания класса.

class stack { int stck[SIZE]; int tos; public: void init(); void push(int i); int pop(); }; void stack::init() { tos = 0; } void stack::push(int i) { if(tos==SIZE) { cout  "Stack is full.\n"; return; } stck[tos] = i; tos++; }

Константный метод объекта

Говорят, что метод объекта обладает свойством неизменности (константности), если после его выполнения состояние объекта не изменяется.Если не контролировать свойство неизменности, то его обеспечение будет целиком зависеть от квалификации программиста. Если же «неизменный» метод в процессе выполнения будет производить посторонние эффекты, то результат может быть самым неожиданным,отлаживать и поддерживать такой код очень тяжело.

Язык С++ позволяет пометить метод как константный. При этом неконстантные методы объекта запрещается использовать в теле помеченного метода, и в контексте этого метода ссылки на сам объект и все его поля будут константны. Для обозначения константности, используется модификатор const.

Также существует возможность пометить ссылку (или указатель) как константную. Применительно к ссылке свойство константности означает, что через эту ссылку можно вызывать только константные методы. Присвоение константной ссылки неконстантной запрещено.

Давайте, рассмотрим пример класса с константными методами:

# include # include using namespace std; class Personal { public: // конструктор с параметрами // мы выделяем здесь память // однако в нашем примере нет // ни деструктора, ни конструктора // копирования - единственная цель, // которую мы преследуем показать // работу константного метода Personal(char*p,char*n,int a){ name=new char[strlen(n)+1]; if(!name){ cout"Error. "; exit(0); } picture_data=new char[strlen(n)+1]; if(!picture_data){ cout"Error. "; exit(0); } strcpy(picture_data,p); strcpy(name,n); age=a; } // Группа константных методов // внутри них невозможно // изменить какое-то из свойств const char*Name()const{ return name; } int Age()const{ return age; } const char*Picture()const{ return picture_data; } void SetName(const char*n){ strcpy(name,n); } void SetAge(int a){ age=a; } void SetPicture(const char*p){ strcpy(picture_data,p); } private: char*picture_data; // путь к фотографии char*name; // имя int age; // возраст }; void main(){ Personal A("C:\\Image\\","Ivan",23); cout"Name: "A.Name()"\n\n"; cout"Age: "A.Age()"\n\n"; cout"Path for picture: "A.Picture()"\n\n"; A.SetPicture("C:\\Test\\"); A.SetName("Leonid"); A.SetAge(90); cout"Name: "A.Name()"\n\n"; cout"Age: "A.Age()"\n\n"; cout"Path for picture: "A.Picture()"\n\n"; }

В данном примере методы Name, Age, Picture объявлены константными. Кроме того, можно наблюдать и использование константных указателей: параметр методов SetName и SetPicture, возвращаемое значение методов Name и Picture. Компилятор обеспечит проверку того, что реализация константных методов не имеет побочных эффектов в виде изменения состояния объекта, реализующего класс Personal. Как только обнаружится попытка выполнить запрещенную операцию, компилятор сообщит об ошибке.

Оператор typeid

Оператор typeid позволяет определить тип объекта во время выполнения.

Результатом typeid является const type_info& . Значение является ссылкой на type_info объект, представляющий идентификатор типа или тип выражения, в зависимости от того, какая форма typeid используется. Дополнительные сведения см. в разделе type_info класса.

Оператор typeid не работает с управляемыми типами (абстрактными деклараторами или экземплярами). Сведения о получении Type указанного типа см. в разделе typeid.

Оператор typeid выполняет проверка во время выполнения при применении к l-значению типа полиморфного класса, где истинный тип объекта не может быть определен статическими сведениями. К таким случаям относятся следующие.

  • Ссылка на класс
  • Указатель, разыменовывая с помощью *
  • Подстрочный указатель ( [ ] ). (Небезопасно использовать подстрок с указателем на полиморфный тип.)

Если выражение указывает на тип базового класса, но объект фактически является типом, производным от этого базового класса, type_info ссылка на производный класс является результатом. Выражение должно указывать на полиморфный тип (класс с виртуальными функциями). В противном случае результатом является type_info статический класс, на который ссылается выражение. Кроме того, указатель должен быть разоменовывлен таким образом, чтобы используемый объект указывал на него. Без разыменовки указателя результатом будет type_info указатель, а не то, что он указывает. Например:

// expre_typeid_Operator.cpp // compile with: /GR /EHsc #include #include class Base < public: virtual void vvfunc() <>>; class Derived : public Base <>; using namespace std; int main() < Derived* pd = new Derived; Base* pb = pd; cout 

Если выражение расшифровывает указатель и значение указателя равно нулю, typeid вызывает исключение bad_typeid. Если указатель не указывает на допустимый объект, __non_rtti_object создается исключение. Он указывает на попытку проанализировать RTTI, которая вызвала ошибку, так как объект каким-то образом недопустим. (Например, это плохой указатель или код не компилировался с помощью /GR.

Если выражение не является указателем, а не ссылкой на базовый класс объекта, результатом является type_info ссылка, представляющая статический тип выражения. Статический тип выражения относится к типу выражения, известному во время компиляции. Семантика исполнения игнорируется при оценке статического типа выражения. Кроме того, ссылки по возможности игнорируются при определении статического типа выражения:

// expre_typeid_Operator_2.cpp #include int main() < typeid(int) == typeid(int&); // evaluates to true >

typeid также можно использовать в шаблонах для определения типа параметра шаблона:

// expre_typeid_Operator_3.cpp // compile with: /c #include template < typename T >T max( T arg1, T arg2 ) < cout arg2 ? arg1 : arg2 ); > 

Можно ли как-то в c++ узнать принадлежность объекта классу?

У меня есть (пока нет но, прежде чем начать нужно определится с такими вещами) массив объектов игры. Это разные классы, которые наследуют от одного (Object, например). То есть все они являются Object.

Но как мне понимать, что элемент этого массива является не только Object в общем, но и, например, игроком или врагом или стеной или еще чем-то?

В js я бы написал:
if (objects[i] instanceof Wall)

Но как такое сделать с С++? При этом без костылей.
Или может быть я не правильно понимаю как такие вещи делаются в С++? Если да, но как это организовывают?

  • Вопрос задан более трёх лет назад
  • 3031 просмотр

Комментировать
Решения вопроса 0
Ответы на вопрос 2
Vitaly @vt4a2h Куратор тега C++
Senior software engineer (C++/Qt/boost)

Зачем вам это? Есть полиморфизм, а касты -- это плохой стиль почти всегда.

Можно много чего использовать, в том числе и тэги. В базовом классе создаёте перечисление, где каждый элемент -- тип наследника + 1 элемент на тип базового класса. В базовом классе определяете виртуальный метод вроде type() и реализуете его для всех наследников.

Но перед тем, как сделать это, хорошо подумайте над тем, надо ли оно вам. Про SOLID почитайте, в особенности про букву L.

Ответ написан более трёх лет назад
Комментировать
Нравится 3 Комментировать

GavriKos

Почитайте например вот это:
https://stackoverflow.com/questions/1986418/typeid.
особенно ответ.
Где то видел еще реализацию через темплейты.
Но 90% что чт то не так с архитектурой раз такое надо. И если уж надо и наследуетесь от базового класса - то сделайте в базовом классе метод, который вернет тип по enum.

Ответ написан более трёх лет назад

Mnemonist

Дима Гашко @Mnemonist Автор вопроса

Согласен, что архитектура, скорее всего не правильная. Но какой она должна быть? Как это правильно организовывать?

ООП, по-идеи, должно абстрагировать реальный мир.
И если, например, я герой игры и передо мной находится какой-то персонаж, то я должен как-то понимать кто это - враг, союзник или просто какой-то житель. Но как это реализовать?

GavriKos

Дима Гашко, спросить у этого объекта - ты враг? Тобишь bool IsEnemy();
Или спросить его тип как вы и описали, но через енам, а не опускаться до типов данных.
Объясню почему на вашем же примере про врагов и союзников.
У вас может быть класс EnemyArcher, EnemyFighter и EnemyBoss. Все они - враги.
И есть метод (дай бог что один), который проверяет враг ли перед нами сравнивая тип данных с вот этими вот тремя классами.
Потом внезапно вы заводите EnemySorcerer. Вам надо не забыть его прописать в методе определения врагов, и - что самое важное - код вам об этом не напоминает! А если у вас все наследуется от BaseObject, у которого есть абстрактный метод IsEnemy (ну или getType(), возвращающий енам) - то вы не забудете его реализовать - иначе просто не скомпилится.

Decadal

И если, например, я герой игры и передо мной находится какой-то персонаж, то я должен как-то понимать кто это - враг, союзник или просто какой-то житель. Но как это реализовать?

ээм
кажется у вас что-то с ооп.

Враг, союзник или житель это роли, которые описывают отношение к игроку.
Иными словами, их "враждебность" или "дружественность" зависит исключительно от поведения, а виды поведений это уже совершенно другой объект.
подумайте в сторону setBehavior(FriendlyBehaviorObject);

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *