Skip to content

Latest commit

 

History

History
328 lines (252 loc) · 6.21 KB

how-to-use-safe-cast-in-cpp-cli.md

File metadata and controls

328 lines (252 loc) · 6.21 KB
descriptiontitlems.datehelpviewer_keywordsms.assetid
Learn more about: How to: Use safe_cast in C++/CLI
How to: Use safe_cast in C++/CLI
11/04/2016
safe_cast keyword [C++], upcasting
0fbc87d8-ecdf-4cd5-81f4-0d8cc18e2aff

How to: Use safe_cast in C++/CLI

This article shows how to use safe_cast in C++/CLI applications. For information about safe_cast in C++/CX, see safe_cast.

Upcasting

An upcast is a cast from a derived type to one of its base classes. This cast is safe and does not require an explicit cast notation. The following sample shows how to perform an upcast, with safe_cast and without it.

// safe_upcast.cpp// compile with: /clrusingnamespaceSystem; interface classA { voidTest(); }; ref structB : publicA { virtualvoidTest() { Console::WriteLine("in B::Test"); } voidTest2() { Console::WriteLine("in B::Test2"); } }; ref structC : publicB { virtualvoidTest() override { Console::WriteLine("in C::Test"); }; }; intmain() { C ^ c = gcnew C; // implicit upcast B ^ b = c; b->Test(); b->Test2(); // upcast with safe_cast b = nullptr; b = safe_cast<B^>(c); b->Test(); b->Test2(); }
in C::Test in B::Test2 in C::Test in B::Test2 

Downcasting

A downcast is a cast from a base class to a class that's derived from the base class. A downcast is safe only if the object that's addressed at runtime is actually addressing a derived class object. Unlike static_cast, safe_cast performs a dynamic check and throws xref:System.InvalidCastException if the conversion fails.

// safe_downcast.cpp// compile with: /clrusingnamespaceSystem; interface classA { voidTest(); }; ref structB : publicA { virtualvoidTest() { Console::WriteLine("in B::Test()"); } voidTest2() { Console::WriteLine("in B::Test2()"); } }; ref structC : publicB { virtualvoidTest() override { Console::WriteLine("in C::Test()"); } }; interface classI {}; value structV : publicI {}; intmain() { A^ a = gcnew C(); a->Test(); B^ b = safe_cast<B^>(a); b->Test(); b->Test2(); V v; I^ i = v; // i boxes V V^ refv = safe_cast<V^>(i); Object^ o = gcnew B; A^ a2= safe_cast<A^>(o); }
in C::Test() in C::Test() in B::Test2() 

safe_cast with user-defined conversions

The next sample shows how you can use safe_cast to invoke user-defined conversions.

// safe_cast_udc.cpp// compile with: /clrusingnamespaceSystem; value structV; ref structR { int x; R() { x = 1; } R(int argx) { x = argx; } staticoperator R::V^(R^ r); }; value structV { int x; staticoperator R^(V& v) { Console::WriteLine("in operator R^(V& v)"); R^ r = gcnew R(); r->x = v.x; return r; } V(int argx) { x = argx; } }; R::operator V^(R^ r) { Console::WriteLine("in operator V^(R^ r)"); return gcnew V(r->x); } intmain() { boolfReturnVal = false; V v(2); R^ r = safe_cast<R^>(v); // should invoke UDC V^ v2 = safe_cast<V^>(r); // should invoke UDC }
in operator R^(V& v in operator V^(R^ r) 

safe_cast and boxing operations

Boxing

Boxing is defined as a compiler-injected, user-defined conversion. Therefore, you can use safe_cast to box a value on the CLR heap.

The following sample shows boxing with simple and user-defined value types. A safe_cast boxes a value type variable that's on the native stack so that it can be assigned to a variable on the garbage-collected heap.

// safe_cast_boxing.cpp// compile with: /clrusingnamespaceSystem; interface structI {}; value structV : publicI { int m_x; V(int i) : m_x(i) {} }; intmain() { // box a value type V v(100); I^ i = safe_cast<I^>(v); int x = 100; V^ refv = safe_cast<V^>(v); int^ refi = safe_cast<int^>(x); }

The next sample shows that boxing has priority over a user-defined conversion in a safe_cast operation.

// safe_cast_boxing_2.cpp// compile with: /clrstaticboolfRetval = true; interface structI {}; value structV : publicI { int x; V(int argx) { x = argx; } staticoperator I^(V v) { fRetval = false; I^ pi = v; returnpi; } }; ref structR { R() {} R(V^ pv) {} }; intmain() { V v(10); I^ pv = safe_cast<I^>(v); // boxing will occur, not UDC "operator I^" }

Unboxing

Unboxing is defined as a compiler-injected, user-defined conversion. Therefore, you can use safe_cast to unbox a value on the CLR heap.

Unboxing is a user-defined conversion, but unlike boxing, unboxing must be explicit—that is, it must be performed by a static_cast, C-style cast, or safe_cast; unboxing cannot be performed implicitly.

// safe_cast_unboxing.cpp// compile with: /clrintmain() { System::Object ^ o = 42; int x = safe_cast<int>(o); }

The following sample shows unboxing with value types and primitive types.

// safe_cast_unboxing_2.cpp// compile with: /clrusingnamespaceSystem; interface structI {}; value structVI : publicI {}; voidtest1() { Object^ o = 5; int x = safe_cast<Int32>(o); } value structV { int x; String^ s; }; voidtest2() { V localv; Object^ o = localv; V unboxv = safe_cast<V>(o); } voidtest3() { V localv; V^ o2 = localv; V unboxv2 = safe_cast<V>(o2); } voidtest4() { I^ refi = VI(); VI vi = safe_cast<VI>(refi); } intmain() { test1(); test2(); test3(); test4(); }

safe_cast and generic types

The next sample shows how you can use safe_cast to perform a downcast with a generic type.

// safe_cast_generic_types.cpp// compile with: /clr interface structI {}; generic<classT> where T:I ref structBase { T t; voidtest1() {} }; generic<classT> where T:I ref structDerived:publicBase <T> {}; ref structR:publicI {}; typedef Base<R^> GBase_R; typedef Derived<R^> GDerived_R; intmain() { GBase_R^ br = gcnew GDerived_R(); GDerived_R^ dr = safe_cast<GDerived_R^>(br); }

See also

safe_cast

close