C++ 关于 dynamic_cast 运算符

C++ 关于 dynamic_cast 运算符

dynamic_cast 用于继承体系中基类指针 (引用) 和派生类指针 (引用) 之间的强制转换

C++ 关于 dynamic_cast 运算符_第1张图片

1. 派生类 向 基类 转换永远是安全的, 即无条件向上转换

没有虚函数

class base {
public:
	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};

派生类向基类可以无条件转换

int main()
{
	base* bp = new base();
	derived* dp = new derived();

	//将 派生类 指针 转换成 基类指针, 两种方式都可以,也不需要多态
	base* bs1 = dynamic_cast<base*>(dp);
	base* bs2 = dp;
	
	return 0;
}

2 基类指针 转换到 派生类(非多态下)

2.1 派生类指针→基类指针→派生类指针

int main()
{
	base* bp = new base();
	derived* dp = new derived();

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dynamic_cast<base*>(dp);
	base* bs2 = dp;
	
	//尝试将 bs1 转换回 派生类指针
	//derived* dd1 = bs1;   //报错: 无法从“base * ”转换为“derived* ”

	// base 没有包含虚函数, 不是多态类型, 无法使用 dynamic_cast
	//derived* dd2 = dynamic_cast(bs1);  //运行时 dynamic_cast 的操作数必须包含多态类类型

	// 直接 static_cast 可行
	derived* dd3 = static_cast<derived*>(bs1);
	dd3->test2();  // 输出derived_test()
	cout << dd3->c << endl;   //输出 3

	return 0;
}

dynamic_cast 必须是在多态 (基类含有虚函数) 下才可行

static_cast 在非多态下可行

2.2 基类指针→派生类指针

假设我们对 bp 指针直接转换, 同样 只有 static_cast 才可行


int main()
{
	base* bp = new base();
	derived* dp = new derived();

	//derived* dd4 = bp;    // 报错
    // dynamic_cast 必须多态
	//derived* dd5 = dynamic_cast(bp);   // 报错 运行时 dynamic_cast 的操作数必须包含多态类类型
	derived* dd6 = static_cast<derived*>(bp);
	dd6->test2();         //输出 derived_test()
	cout<<dd6->c<<endl;   //输出 -33686019, 值未定义

	return 0;
}

这个地方也不是特别能理解为什么 static_cast 可以成功???

3 基类指针 转换到 派生类(多态下)

现在给 基类 base 增加一个虚函数

class base {
public:

	//增加 虚函数
	virtual void func1() {
		cout << "base_func1()" << endl;
	}

	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};

3.1 派生类指针→基类指针→派生类指针

int main()
{
	base* bp = new base();
	derived* dp = new derived();

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dynamic_cast<base*>(dp);
	base* bs2 = dp;
	
	//尝试将 bs1 转换回 派生类指针
	//derived* dd1 = bs1;   //报错: 无法从“base * ”转换为“derived* ”

	// base 包含虚函数, 多态类型, 可以正确使用 dynamic_cast
	derived* dd2 = dynamic_cast<derived*>(bs1);  //运行时 dynamic_cast 的操作数必须包含多态类类型
	if (dd2)
	{
		dd2->test2();             // 输出 derived_test()
		cout << dd2->c << endl;   // 输出 3
	}
	// 直接 static_cast 依然可行
	derived* dd3 = static_cast<derived*>(bs1);
	if (dd3)
	{
		dd3->test2();             // 输出 derived_test()
		cout << dd3->c << endl;   // 输出 3
	}

	return 0;
}

多态情况下,dynamic_cast 可以运行通过了

static_cast 一直可以通过,是不是多态都可以

3.2 基类指针→派生类指针

仍然尝试将 基类指针bp 转换成 派生类指针

int main()
{
	base* bp = new base();
	derived* dp = new derived();
	
	derived* dd5 = dynamic_cast<derived*>(bp);
	if (dd5)
	{
		dd5->test2();
		cout<<dd5->c << endl;
	}
	else                            // dd5 为空 输出 "空指针"
	{
		cout << "空指针" << endl;
	}

    
	derived* dd6 = static_cast<derived*>(bp);
	if (dd6)               // dd6 非空  
	{
		dd6->test2();             // 输出  derived_test()
		cout << dd6->c << endl;   // 输出  -33686019
	}
	else
	{
		cout << "空指针" << endl;
	}

	return 0;
}

可能是这样:dynamic_cast 专门用于继承中的基类、派生类转换,而且得是多态 (虚函数) 场景下

  • 派生类指针→基类指针→派生类指针

第 1 次转换毫无疑问,第 2 次转换 dynamic_cast 检查 该基类指针 的动态类型 是 派生类,则 dynamic_cast 成功转换,因为该基类指针确实指向了一个派生类对象,转换是安全的。当然用 static_cast 转换也可行。

  • 基类指针→派生类指针

这种情况下,基类指针实际上指向的是基类对象,

虽然 static_cast= 可以强制实现这种转换,但是不合我们的理解,不过转换后确实可以访问派生类的函数,以及其它成员(这应该要从内存角度考虑了,有机会再探索)

相反,dynamic_cast 发现这个指针并没有指向派生类对象,所以没有进行转换,而是返回了nullptr,所以确实是 dynamic_cast 的处理更合理一点 (根本就没有指向派生类,那就不要转换了),避免后续可能出错。

4 小结

派生类指针 向 基类指针 转换,无条件可行

基类指针指向派生类对象情况下,要实现基类指针 转换到 派生类指针

  • static_cast 可行,不需要多态
  • dynamic_cast 一定要多态,基类要有虚函数

基类指针指向基类对象情况下,要实现基类指针 转换到 派生类指针

  • static_cast 可以成功,但是不太合乎理解
  • dynamic_cast 一定要多态,基类要有虚函数,无法转换,返回 nullptr

note: 凡是基类指针到派生类指针的转换,dynamic_cast 都需要多态

引用和指针的情况类似,dynamic_cast 中,指针转换失败返回nullptr,但是引用无法是空引用,所以引用转换失败时返回 std::bad_cast 异常(这里都是指 基类 向 派生类 转换)

5 typeid 运算符

typeid 和 dynamic_cast 实现 运行时类型识别 (run-time type identification ,RTTI)功能的 2 个重要运算符

typeid 用于得到对象的类型,是否多态会影响到 typeid 的结果

同样使用刚才的 测试 类型 base 和 derived

5.1 非多态场景下

class base {
public:

	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};

非多态下,typeid 的结果和 静态类型保持一致

int main()
{
	//基类对象指针
	base* bp = new base();
	cout << typeid(*bp).name() << endl;   //输出  class base

	//派生类对象指针
	derived* dp = new derived();
	cout << typeid(*dp).name() << endl;   //输出  class derived

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dp;
	base* bs2 = dynamic_cast<base*>(dp);
	cout << typeid(*bs1).name() << endl;   //非多态下输出  class base
	cout << typeid(*bs2).name() << endl;   //非多态下输出  class base

	return 0;
}

note: typeid() 返回的结果是一种 type_info 的对象,还要调用 name() 函数获得具体的类型名称

可以看到,尽管 基类指针 bs1 和 bs2 都指向了派生类对象,但是结果依然是 静态类型,也就是定义 时 使用的类型

5.2 多态场景下

class base {
public:

	//增加 虚函数
	virtual void func1() {
		cout << "base_func1()" << endl;
	}

	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};

再次运行刚刚的测试代码

int main()
{
	//基类对象指针
	base* bp = new base();
	cout << typeid(*bp).name() << endl;   //输出  class base

	//派生类对象指针
	derived* dp = new derived();
	cout << typeid(*dp).name() << endl;   //输出  class derived

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dp;
	base* bs2 = dynamic_cast<base*>(dp);
	cout << typeid(*bs1).name() << endl;   //输出  class derived
	cout << typeid(*bs2).name() << endl;   //输出  class derived

	return 0;
}

此时 bs1 和 bs2 的 typeid 的结果都是 派生类 类型,

按照书里的说法,多态场景下,* bs1 和 * bs2 会在运行时求值,由于它们指向派生类,所以得到派生类的结果

6 完整代码

dynamic_cast

#include

using namespace std;

class base {
public:

	//增加 虚函数
	virtual void func1() {
		cout << "base_func1()" << endl;
	}

	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};


int main()
{
	base* bp = new base();
	derived* dp = new derived();

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dynamic_cast<base*>(dp);
	base* bs2 = dp;
	
	//尝试将 bs1 转换回 派生类指针
	//derived* dd1 = bs1;   //报错: 无法从“base * ”转换为“derived* ”

	// base 包含虚函数, 多态类型, 可以正确使用 dynamic_cast
	//derived* dd2 = dynamic_cast(bs1);  //运行时 dynamic_cast 的操作数必须包含多态类类型
	//if (dd2)
	//{
	//	dd2->test2();
	//	cout << dd2->c << endl;
	//}
	// 直接 static_cast 可行
	//derived* dd3 = static_cast(bs1);
	//if (dd3)
	//{
	//	dd3->test2();
	//	cout << dd3->c << endl;
	//}

	//derived* dd4 = bp;   // 报错
	
	// dynamic_cast 必须多态
	//derived* dd5 = dynamic_cast(bp);  //运行时 dynamic_cast 的操作数必须包含多态类类型
	
	//if (dd5)
	//{
	//	dd5->test2();
	//	cout<c << endl;
	//}
	//else
	//{
	//	cout << "空指针" << endl;
	//}

	//derived* dd6 = static_cast(bp);
	//if (dd6)
	//{
	//	dd6->test2();
	//	cout << dd6->c << endl;
	//}
	//else
	//{
	//	cout << "空指针" << endl;
	//}

	return 0;
}

typeid

#include

using namespace std;

class base {
public:

	//增加 虚函数
	virtual void func1() {
		cout << "base_func1()" << endl;
	}

	void test() {
		cout << "base_test()" << endl;
	}
protected:
	int a=1;

};

class derived:public base {

public:
	void test2()
	{
		cout << "derived_test()" << endl;
	}
	int c = 3;
protected:
	int b=2;
};


int main()
{
	//基类对象指针
	base* bp = new base();
	cout << typeid(*bp).name() << endl;   //输出  class base

	//派生类对象指针
	derived* dp = new derived();
	cout << typeid(*dp).name() << endl;   //输出  class derived

	//将 派生类 指针 转换成 基类指针, 两种方式都可以
	base* bs1 = dp;
	base* bs2 = dynamic_cast<base*>(dp);

    //非多态下输出  class base
    //多态下输出   class derived
	cout << typeid(*bs1).name() << endl;
    
    //非多态下输出  class base
    //多态下输出   class derived
	cout << typeid(*bs2).name() << endl;   

	return 0;
}

你可能感兴趣的:(c++)