スマートポインタで確保した派生クラスを安全にダウンキャストする方法。

当記事はダウンキャストについての知識が前提となりますので、知らない方は下記の記事を先に読むことをお勧めします。
https://qiita.com/maihei541/items/cff2e66540f83aacd213#comment-402aa67ebd4ac1e28147
# dynamic_castでダウンキャストするとエラーになる
安全にダウンキャストを行うには、``dynamic_cast``を活用するのでしたね。
とりあえずそれっぽくコードを書いてみます。
```c++:dynamic_cast1.cpp
#include <iostream>
//基底クラス
class Super {
public:
	//クラス名を見るだけの純粋仮想関数
	virtual void ShowClassName() = 0;
};

//派生クラス1
class Sub1 : public Super {
private:
	int value = 1;
public:
	void ShowClassName() override {
		std::cout << "Sub1" << std::endl << std::endl;
	}
	//int型の値を表示するメソッド
	void ShowIntValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//派生クラス2
class Sub2 : public Super {
private:
	float value = 1.0f;
public:
	void ShowClassName() override {
		std::cout << "Sub2" << std::endl << std::endl;
	}
	//float型の値を表示するメソッド
	void ShowFloatValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//メイン関数
int main() {
	//アップキャストでSub1とSub2のインスタンスをSuper型のポインタに格納
	std::shared_ptr<Super> sub1 = std::make_shared<Sub1>();
	std::shared_ptr<Super> sub2 = std::make_shared<Sub2>();
	//ポリモーフィズムでSuper型のポインタからShowClassNameを呼び出す
	sub1->ShowClassName(); // 出力: Sub1
	sub2->ShowClassName(); // 出力: Sub2
	//派生クラスにしかないメソッドを呼びたい。
	//ダウンキャストでSuper型のポインタをSub1型に変換。
	Sub1* down_sub1 = dynamic_cast<std::shared_ptr<Sub1>>(sub1);//sub1をSub1にダウンキャスト
	//sub1にtry catch構文で例外処理を行う。
	try {
		if (down_sub1 == nullptr) {
			// 例外をスロー
			throw std::runtime_error("down_sub1のダウンキャストでエラーが発生しました！");
		}
		down_sub1->ShowIntValue(); // 出力: 1
	}
	catch (const std::runtime_error& e) {
		// 例外をキャッチして処理
		std::cerr << "例外キャッチ: " << e.what() << std::endl;
	}

	system("pause");
	return 0;
}
```
```:error
E0695 : dynamic_cast 内の型は、完全なクラス型へのポインターまたは参照であるか、void * である必要があります。 47行目
C2680 : 'std::shared_ptr<Sub1,std::default_delete<Sub1>>': dynamic_cast のターゲット型が無効です。 47行目
```
エラーが出てしまいました。
``std::shared_ptr``などのスマートポインタは、生のポインターというわけではなく、ポインタの所有権を持ち、``*``の演算子オーバーロードで、間接参照を行うクラスでした。
実は、``dynamic_cast``は、生のポインタしか扱えないのです。
## メンバ関数のget()でdynamic_castに生のポインタを渡してみる。
```c++:dynamic_cast.cpp
#include <iostream>
//基底クラス
class Super {
public:
	//クラス名を見るだけの純粋仮想関数
	virtual void ShowClassName() = 0;
};

//派生クラス1
class Sub1 : public Super {
private:
	int value = 1;
public:
	void ShowClassName() override {
		std::cout << "Sub1" << std::endl << std::endl;
	}
	//int型の値を表示するメソッド
	void ShowIntValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//派生クラス2
class Sub2 : public Super {
private:
	float value = 1.0f;
public:
	void ShowClassName() override {
		std::cout << "Sub2" << std::endl << std::endl;
	}
	//float型の値を表示するメソッド
	void ShowFloatValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//メイン関数
int main() {
	//アップキャストでSub1とSub2のインスタンスをSuper型のポインタに格納
	std::shared_ptr<Super> sub1 = std::make_shared<Sub1>();
	std::shared_ptr<Super> sub2 = std::make_shared<Sub2>();
	//ポリモーフィズムでSuper型のポインタからShowClassNameを呼び出す
	sub1->ShowClassName(); // 出力: Sub1
	sub2->ShowClassName(); // 出力: Sub2
	//派生クラスにしかないメソッドを呼びたい。
	//ダウンキャストでSuper型のポインタをSub1型に変換。
	std::shared_ptr<Sub1> down_sub1(dynamic_cast<Sub1*>(sub1.get()));//sub1をSub1にダウンキャスト
	//sub1にtry catch構文で例外処理を行う。
	try {
		if (down_sub1 == nullptr) {
			// 例外をスロー
			throw std::runtime_error("down_sub1のダウンキャストでエラーが発生しました！");
		}
		down_sub1->ShowIntValue(); // 出力: 1
	}
	catch (const std::runtime_error& e) {
		// 例外をキャッチして処理
		std::cerr << "例外キャッチ: " << e.what() << std::endl;
	}

	system("pause");
	return 0;
}
```
```:result
Sub1

Sub2

1

続行するには何かキーを押してください . . .
```
一見成功したように見えました。
しかしこのまま続行すると
```:error
ブレークポイント命令 (__debugbreak() ステートメントまたは類似の呼び出し) が dynamic_cast.exe で実行されました。
```
エラー箇所
```C++:delete_scaler.cpp
//
// delete_scalar.cpp
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines the scalar operator delete.
//
#include <crtdbg.h>
#include <malloc.h>
#include <vcruntime_new.h>
#include <vcstartup_internal.h>

////////////////////////////////////////////////////////////////
// delete() Fallback Ordering
//
// +-------------+
// |delete_scalar<----+-----------------------+
// +--^----------+    |                       |
//    |               |                       |
// +--+---------+  +--+---------------+  +----+----------------+
// |delete_array|  |delete_scalar_size|  |delete_scalar_nothrow|
// +--^----^----+  +------------------+  +---------------------+
//    |    |
//    |    +-------------------+
//    |                        |
// +--+--------------+  +------+-------------+
// |delete_array_size|  |delete_array_nothrow|
// +-----------------+  +--------------------+

_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept
{
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK);//ここでエラー
    #else
    free(block);
    #endif
}

```
どうやら、解放時にエラーが起こってしまうようです。
これは
```C++:
std::shared_ptr<Sub1> down_sub1(dynamic_cast<Sub1*>(sub1.get()));//sub1をSub1にダウンキャスト
```
上記の方法では、参照カウンタが共有されないのです。
# どうすればよかったのか
C++のstdには``dynamic_pointer_cast``という関数が存在しており、これを使用することで正常な参照カウンタで``shared_ptr``のダウンキャストを行うことが可能です。
```C++:dynamic_pointer_cast.cpp
#include <iostream>
//基底クラス
class Super {
public:
	//クラス名を見るだけの純粋仮想関数
	virtual void ShowClassName() = 0;
};

//派生クラス1
class Sub1 : public Super {
private:
	int value = 1;
public:
	void ShowClassName() override {
		std::cout << "Sub1" << std::endl << std::endl;
	}
	//int型の値を表示するメソッド
	void ShowIntValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//派生クラス2
class Sub2 : public Super {
private:
	float value = 1.0f;
public:
	void ShowClassName() override {
		std::cout << "Sub2" << std::endl << std::endl;
	}
	//float型の値を表示するメソッド
	void ShowFloatValue() {
		std::cout << value << std::endl << std::endl;
	}
};

//メイン関数
int main() {
	//アップキャストでSub1とSub2のインスタンスをSuper型のポインタに格納
	std::shared_ptr<Super> sub1 = std::make_shared<Sub1>();
	std::shared_ptr<Super> sub2 = std::make_shared<Sub2>();
	//ポリモーフィズムでSuper型のポインタからShowClassNameを呼び出す
	sub1->ShowClassName(); // 出力: Sub1
	sub2->ShowClassName(); // 出力: Sub2
	//派生クラスにしかないメソッドを呼びたい。
	//ダウンキャストでSuper型のポインタをSub1型に変換。
	std::shared_ptr<Sub1> down_sub1 = std::dynamic_pointer_cast<Sub1>(sub1);//sub1をSub1にダウンキャスト
	//sub1にtry catch構文で例外処理を行う。
	try {
		if (down_sub1 == nullptr) {
			// 例外をスロー
			throw std::runtime_error("down_sub1のダウンキャストでエラーが発生しました！");
		}
		down_sub1->ShowIntValue(); // 出力: 1
	}
	catch (const std::runtime_error& e) {
		// 例外をキャッチして処理
		std::cerr << "例外キャッチ: " << e.what() << std::endl;
	}

	system("pause");
	return 0;
}
```
```:result
Sub1

Sub2

1

続行するには何かキーを押してください . . .
```
正しく実行され、この方法なら参照カウンタが正しく動作するため、このまま続行したり、解放が行われても問題が起こることはありません。
# 総括
・スマートポインタに対して``dynamic_cast``をそのまま行うと危険な動作が行われてしまう。
・``shared_ptr``に確保した派生クラスをダウンキャストする際は``dynamic_pointer_cast``を使用しよう。