[C/C++]_[初级]_[static_cast,reinterpret_cast,dynimic_cast的使用场景和区别]

场景:

1. C++的对象区别于C的原因是他们可以有继承关系, 方法有重载, 覆盖关系等, 他们的对象内存数据结构因此也比较复杂.

2. 很多情况下我们需要一个父类来存储子类的指针对象进行通用方法的操作,涉及到具体某个子类对象特定操作时又需要强制转换为子类,那么这时候该用什么好呢?

答: 如果不需要类型检查的话就直接用C的强制转换即可(B*)c. 但是C++ 之父并不推荐使用C的强制类型转换;

原因1是没有编译器检查.

原因2是对象指针在子类父类之间转换时所对应的地址值可能会变化, 这样用C的转换会有误导的可能在那里.

看例子和注释说明吧:

test.cpp

#include <iostream>

class A
{
public:
	A(){}
	~A(){}
	int i;
	int j1;
	void Test(){ std::cout << "TestA" << std::endl;}
	/* data */
};

class C
{
public:
	C(){}
	~C(){}
	int j;
	void Test(){ std::cout << "TestC" << std::endl;}
	/* data */
};

class B : public A,public C
{
public:
	B(){}
	~B(){}
	void Test(){ std::cout << "TestB" << std::endl;}
	/* data */
};

class K
{

};

// static_cast: 可以进行类型上行和下行转换,会进行类型检查.
// reinterpret_cast: 和C的强转一样,不做类型检查,可以从任意类型转换为其他类型.
// dynimic_cast:  只适用于多态的类(带virtual)
int main(int argc, char const *argv[])
{
	A* a = new A();
	B* b = new B();
	C* c = b;

	std::cout << "(int*)a* :" << (int*)a << " (int)a:" << (int)a << " reinterpret_cast<int>(a):"
		      << reinterpret_cast<int>(a) << std::endl;

	std::cout << "(int*)b :" << (int*)b << " (int)b:" << (int)b << " reinterpret_cast<int>(b):"
		      << reinterpret_cast<int>(b) << std::endl;

	// 1.这里如果把c转换为(K*)c,编译不会报错,但是如果使用static_cast<K*>编译会报错.
	// 因为static_cast会进行上行的类型检查.
    // 注意: 这里(int*)c的地址和上边的(int*)b地址是不一样的,因为他们不是多态关系,而且A,C有成员变量,因此会有偏移量.(没有virtual)
	std::cout << "(int*)c :" << (int*)c << " (int)c:" << (int)c << " reinterpret_cast<int>(c):"
		      << reinterpret_cast<int>(c) <<  " (B*)c: " << (B*)c << " static_cast<B*>(c):"
		      << static_cast<B*>(c) << " static_cast<C*>(b):"
		      << static_cast<C*>(b)
		      << std::endl;

    // 以下编译会报错,dynimc_cast不允许非多态转换,因为没有virtual
	// ////error: cannot dynamic_cast ‘c‘ (of type ‘class C*‘) to type ‘class B*‘ (source type is not polymorphic)
	// 如果C的构造函数加virtual的话是可以转的,而且带virtual表的地址不会变.
	// std::cout << "c* :" << (int*)c << ":" << (int)c << ":"
	// 	      << dynamic_cast<B*>(c) << ":"
	// 	      << std::endl;
	return 0;
}

输出:

(int*)a* :0x2c64e0 (int)a:2909408 reinterpret_cast<int>(a):2909408
(int*)b :0x2c2378 (int)b:2892664 reinterpret_cast<int>(b):2892664
(int*)c :0x2c2380 (int)c:2892672 reinterpret_cast<int>(c):2892672 (B*)c: 0x2c2378 static_cast<B*>(c):0x2c2378 static_cast<C*>(b):0x2c2380

所以你看到以下static_cast的用法不要觉得奇怪, 它是为了使用编译器检查.

    template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
    {
        static_cast<IUnknown*>(*pp);    // make sure everyone derives from IUnknown
        return reinterpret_cast<void**>(pp);
    }

参考:

http://www.cnblogs.com/chio/archive/2007/07/18/822389.html

时间: 2024-08-03 17:10:56

[C/C++]_[初级]_[static_cast,reinterpret_cast,dynimic_cast的使用场景和区别]的相关文章

[C/C++]_[0基础]_[static_cast,reinterpret_cast,dynimic_cast的使用场景和差别]

场景: 1. C++的对象差别于C的原因是他们能够有继承关系, 方法有重载, 覆盖关系等, 他们的对象内存数据结构因此也比較复杂. 2. 非常多情况下我们须要一个父类来存储子类的指针对象进行通用方法的操作.涉及到详细某个子类对象特定操作时又须要强制转换为子类.那么这时候该用什么好呢? 答: 假设不须要类型检查的话就直接用C的强制转换就可以(B*)c. 可是C++ 之父并不推荐使用C的强制类型转换; 原因1是没有编译器检查. 原因2是对象指针在子类父类之间转换时所相应的地址值可能会变化, 这样用C

[Cocoa]_[初级]_[NSTableView--数据操作和表格操作要注意的问题]

1.首先在MainMenu.lib文件里面创建一个NSTableView,在界面上显示一个表格出来,并对表格进行设置. 2.创建文件TableViewDelegate.h和TableViewDelegate.m(文件内容如下文件所示).并在MainMenu.lib文件中创建一个Object(从窗口右边选中Object,拉到在Window中的对话框中),命名:TableViewDelegate. 3.设置代理:tableView 进行设置,和图片中的Table View进行连接. staticFi

[libcurl]_[初级]_[使用libcurl下载大文件]

场景: 1. 在Windows编程时, 下载http页面(html,xml)可以使用winhttp库,但是并不是很下载文件,因为会失败. 由此引出了WinINet库,无奈这个库的稳定性比较低,使用例子又少, 下载大文件时经常是不完整,可查找的资料很少或者是没有特殊情况的解决办法. 2. 我的原则是如果系统有自带的就用系统的,但是 WinINet 要掌握需要花不少时间. 时间因素考虑到了libcurl. 3. libcurl支持ftp,http等协议的文件读取,还能自动获取文件大小, 最重要的是不

[C/C++标准库]_[初级]_[计算结构体成员的偏移量]

场景: 1. C结构体里计算结构体的偏移量平常看来没什么必要,但是放到插件结构的设计里就有必要了,比如只能使用偏移量访问的场景,而不能使用引用成员变量的场景. 2. 在设计一致性的接口时,公用的接口不怎么变化的,但是插件模块的结构可以不需要根据统一结构来设计,他们只需要提供偏移量给公用接口调用就行了,不同的插件 可能偏移量不一致,因为他们可以独立实现.公用接口就可以通过偏移量来访问不同的变量. 3. 可以使用stddef.h文件里的  offsetof /* Define offsetof ma

[C/C++不常见语法特性]_[初级]_[左值-右值-lvalue-rvalue]

参考:1. http://en.cppreference.com/w/cpp/language/value_category   << Value categories >> 2. https://msdn.microsoft.com/en-us/library/dd293668.aspx   << Rvalue Reference Declarator: && >>3. https://msdn.microsoft.com/en-us/li

[WTL/ATL]_[初级]_[如何使用GetOpenFileName多选文件-根据文件名长度计算lpstrFile长度]

场景: 1. 使用GetOpenFileName 时, 需要预先自定义lpstrFile的长度比如,buf[1024], 但是如果选择的文件过多怎么办?总不能创建一个超大的内存空间吧, 如果选择少时又浪费内存. 2. 微软的MSDN的坏处就是不提供实际的例子,而在别的地方提供,难道他们没遇到这类普通的问题? 3. 这里stackoverflow提供了一个微软使用lpfnHook 的例子来解决这个问题,这个例子对于unicode是有问题的,计算长度没有x2. 这个bug困扰了我半天. 找这个解决方

[C/C++11语法]_[初级]_[lamba 表达式介绍]

场景 lambda 表达式在很多语言里都有一席之地,因为它的原因,可以在函数里快速定义一个便携的函数,或者在函数参数里直接快速构造和传递. 它可以说是匿名函数对象,一般只适用于某个函数内,只做临时使用. 一般是需要在对某个数据临时特殊处理时使用,比如对某种参数类型进行限定的再次封装和行为约束. 参考 1. C# Lambda表达式及其优势 2. Lambda Expressions in C++ 3. Exception Specifications (throw) (C++) 4. noexc

[Java]_[初级]_[String的split里的坑]

场景: 1. 有时候需要使用某些字符来作为值的合并存储,比如以@作为分隔符, [email protected]@323232, 用一个属性存储这3个值, 用的时候再拿出来split就可以了. 2. 问题是有时候某个值会缺失,比如第一个值没有的情况, @[email protected]  第2,3个值没有的情况 [email protected]@ ,当然也期望是能返回3个值,只是后边2个值为空字符就行了. 事实上,结果不是这样,即使 2,3个值没有的情况 [email protected]@

[Windows]_[初级]_[创建独立子进程和读取子进程的输出]

场景: 1.  有一些外部工具命令需要通过程序调用,比如启动服务器或者使用网络命令获取输出. 2.  使用了匿名管道CreatePipe获取子进程输出. 参考: 1.  MSDN的主题<Creating a Child Process with Redirected Input and Output>. 2.  <Windows核心编程>进程章节. 代码: #include "test_shellrun.h" #include <stdlib.h>