C++强制类型转换
dynamic_cast
回答的问题:是否能将某个对象的地址安全地赋值给一个特定类型的指针;同时也回答了强制类型转换是否安全的问题。
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行安全的向下转型(safe downcasting),两种情况:
- 基类指针所指对象是派生类类型的,将其转换为派生类型的指针,是安全的,转换成功。
- 基类指针所指对象为基类类型,要将其转换为派生类型指针,这是不安全的,转换失败。
向上转型本身就是安全的,即基类指针可以指向派生类指针,可以直接使用=符号,无需使用dynamic_cast运算符。
格式:dynamic_cast<Type *>(pt)
pt能否安全的类型转换(*pt为Type或者*pt为Type的继承类)为Type *,如果可以,则返回pg的地址;反之,返回0
也可以使用引用(应该不常用):dynamic_cast<Superb &>(rg)
pt能否安全的类型转换(pt为Type或者pt为Type的继承类)为Type &,如果可以,则返回pg的地址;反之,引发bad_cast异常(在typeinfo头文件中)
const_cast
- 主要用途:对于常量指针,移除对象的常量性。
- 转换格式:const_cast < type-name > (expression)
- 要求:type-name和expression必须是同一种类型,他们只在const和volatile上存在区别,转换成功就赋值就行,转换失败则返回0。
- volatile:提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
- 与常规类型转换的不同:常规类型转换可以转换为不同类型,但是const_cast不允许不同类型的转换
- 基本原则:不允许将定义为const的变量转换为非const类型(这个是针对非指针)。
分几种情况:其实就是基本原则的体现
- 原来就是常量,用一个常量指针指向它,要将这个常量指针转换为非常量指针并修改其值,这是不允许的。
- 原来不是常量,用一个常量指针指向它,要将这个常量指针转换为非常量指针并修改其值,这是允许的。
static_cast
格式:static_cast < type-name > (expression)
当且仅当type-name类型可以隐式转换为expression类型,或反之,否则类型转换会报错,两者能互相转换
允许的转换
- 允许向上转换(继承类指针转换为基类指针)
- 允许向下转换(基类指针转换为继承类指针)
- 由于枚举类可以隐式转换为int,因此static_cast允许这类转换,并且允许将int类型转换为枚举类
- 允许将double转换为int,将int转换为double
- 允许将float转换为long,允许将long转换为float
不允许的转换
- 不同类型的指针之间互相转换
- 非指针类型和指针类型之间的相互转换
- 不同类型的引用之间的转换
reinterpret_cast
格式:reinterpret_cast < type-name > (expression)
转换时,执行的是逐个比特复制的操作。转换的安全性由程序员负责。
允许的转换:
- 可以将指针类型转换为能够容纳该指针类型的整型,反之不能转换。
- 不同类型的指针之间的转换
- 不同类型的引用之间的转换
不允许的转换:
- 不能将函数指针转换为数据指针
代码
rtti1.h
#pragma once
#include <iostream>
using std::cout;
#ifndef _RTTI1_H_
#define _RTTI1_H_
class Grand
{
private:
int hold;
public:
Grand(int h = 0) : hold(h) {}
virtual void Speak() const { cout << "I am a grand class!\n"; }
virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
Superb(int h = 0) : Grand(h) {}
void Speak() const { cout << "I am a superb class!!\n"; }
virtual void Say() const
{
cout << "I hold the superb value of " << Value() << "!\n";
}
};
class Magnificent : public Superb
{
private:
char ch;
public:
Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
void Speak() const { cout << "I am a magnificent class!!!\n"; }
void Say() const
{
cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";
}
};
#endif
main.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "rtti1.h"
using std::cout;
using std::endl;
Grand *GetOne();
void change(const int *pt, int n);
int main()
{
/*
dynamic_cast:
dynamic_cast是最常用的RTTI操作符。
回答的问题:是否能将某个对象的地址安全地赋值给一个特定类型的指针;同时也回答了强制类型转换是否安全的问题
格式:dynamic_cast<Type *>(pt)
pt能否安全的类型转换(*pt为Type或者*pt为Type的继承类)为Type *,如果可以,则返回pg的地址;反之,返回0
也可以使用引用(应该不常用):dynamic_cast<Superb &>(rg)
pt能否安全的类型转换(pt为Type或者pt为Type的继承类)为Type &,如果可以,则返回pg的地址;反之,引发bad_cast异常(在typeinfo头文件中)
*/
cout << "dynamic_cast***************************************************************" << endl;
std::srand(std::time(0));
Grand *pg;
Superb *ps;
for (int i = 0; i < 5; i++)
{
pg = GetOne();
pg->Speak();
if (ps = dynamic_cast<Superb *>(pg)) // 如果可以转换,则if条件判断为1,则执行ps->Say();反之,不执行
ps->Say();
}
/*
const_cast:
转换格式:const_cast < type-name > (expression)
要求:type-name和expression必须是同一种类型,他们只在const和volatile上存在区别,转换成功就赋值就行,转换失败则返回0
与常规类型转换的不同:常规类型转换可以转换为不同类型,但是const_cast不允许不同类型的转换
基本原则:不允许将定义为const的变量转换为非const类型。
*/
cout << "const_cast***************************************************************" << endl;
int pop1 = 38383;
const int pop2 = 2000;
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
change(&pop1, -103); // 转换了,因为pop1是非const类型
change(&pop2, -103); // 没转换,因为pop2是const类型
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
/*
static_cast:
格式:static_cast < type-name > (expression)
当且仅当type-name类型可以隐式转换为expression类型,或反之,否则类型转换会报错,两者能互相转换
1.允许向上转换(继承类指针转换为基类指针)
2.允许向下转换(基类指针转换为继承类指针)
3.由于枚举类可以隐式转换为int,因此static_cast允许这类转换,并且允许将int类型转换为枚举类
4.允许将double转换为int,将int转换为double
5.允许将float转换为long,允许将long转换为float
*/
cout << "static_cast***************************************************************" << endl;
Grand x = Grand(10); // 基类对象
Grand *w = &x; // 基类指针
Superb z = Superb(11); // 继承类对象
Superb *pointer1 = &z;
Superb *y = static_cast<Superb *>(w); // 将基类指针转换为继承类指针 允许
y->Speak(); // 只是允许但是不推荐,因为转换为继承类指针后可以调用继承类的成员方法,而基类没有该成员方法,会引发异常
Grand *a = static_cast<Grand *>(pointer1); // 将继承类指针转换为基类指针
a->Speak();
int varint1 = 99;
double vardouble1 = 99.99;
int var = static_cast<int>(vardouble1);
std::cout << "var = " << var << std::endl;
double var1 = static_cast<double>(varint1);
std::cout << std::fixed << "var1 = " << var1 << std::endl;
/*
reinterpret_cast:
格式:reinterpret_cast < type-name > (expression)
可以将指针类型转换为能够容纳该指针类型的整型,反之不能转换。
不能将函数指针转换为数据指针
*/
struct dat
{
short a;
short b;
};
long value = 0xA224B118;
dat *pd = reinterpret_cast<dat *>(&value);
std::cout << std::hex << pd->a; // display first 2 bytes of value
return 0;
}
Grand *GetOne() // generate one of three kinds of objects randomly
{
Grand *p = 0;
switch (std::rand() % 3)
{
case 0:
p = new Grand(std::rand() % 100);
break;
case 1:
p = new Superb(std::rand() % 100);
break;
case 2:
p = new Magnificent(std::rand() % 100,
'A' + std::rand() % 26);
break;
}
return p;
}
void change(const int *pt, int n)
{
int *pc;
pc = const_cast<int *>(pt);
*pc += n;
}
运行结果:
C:\Users\15495\Documents\Jasmine\Work\coding\cmake-build-debug\coding.exe
dynamic_cast***************************************************************
I am a magnificent class!!!
I hold the character A and the integer 23!
I am a superb class!!
I hold the superb value of 8!
I am a superb class!!
I hold the superb value of 54!
I am a magnificent class!!!
I hold the character R and the integer 72!
I am a magnificent class!!!
I hold the character Q and the integer 65!
const_cast***************************************************************
pop1, pop2: 38383, 2000
pop1, pop2: 38280, 2000
50
100
static_cast***************************************************************
I am a grand class!
I am a superb class!!
var = 99
var1 = 99.000000
b118
Process finished with exit code 0
智能指针强制类型转换
dynamic_pointer_cast
适用对象:shared_ptr。
格式:std::shared_ptr<Superb> ps = std::dynamic_pointer_cast<Superb>(pa)
函数原理:首先,把指针拿出来,然后通过dynamic转化指针类型,然后再生成对应的智能指针,三个步骤。源码如下:
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
dynamic_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
using _Sp = shared_ptr<_Tp>;
if (auto* __p = dynamic_cast<typename _Sp::element_type*>(__r.get()))
return _Sp(__r, __p);
return _Sp();
}
如果需要对std::unique_ptr
进行转换,可以模仿这个方式自己写,在标准库中,并没有提供。
const_pointer_cast
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
const_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
using _Sp = shared_ptr<_Tp>;
return _Sp(__r, const_cast<typename _Sp::element_type*>(__r.get()));
}
static_pointer_cast
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
static_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
using _Sp = shared_ptr<_Tp>;
return _Sp(__r, static_cast<typename _Sp::element_type*>(__r.get()));
}
reinterpret_pointer_cast
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
reinterpret_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
using _Sp = shared_ptr<_Tp>;
return _Sp(__r, reinterpret_cast<typename _Sp::element_type*>(__r.get()));
}
代码
rtti1.h
#pragma once
#include <iostream>
using std::cout;
#ifndef _RTTI1_H_
#define _RTTI1_H_
class Grand
{
private:
int hold;
public:
Grand(int h = 0) : hold(h) {}
virtual void Speak() const { cout << "I am a grand class!\n"; }
virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
Superb(int h = 0) : Grand(h) {}
void Speak() const { cout << "I am a superb class!!\n"; }
virtual void Say() const
{
cout << "I hold the superb value of " << Value() << "!\n";
}
};
class Magnificent : public Superb
{
private:
char ch;
public:
Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
void Speak() const { cout << "I am a magnificent class!!!\n"; }
void Say() const
{
cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";
}
};
#endif
main.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "rtti1.h"
#include <Memory>
using std::cout;
using std::endl;
std::shared_ptr<Grand> GetOne();
void change(const std::shared_ptr<int> pt, int n);
int main()
{
cout << "dynamic_pointer_cast***************************************************************" << endl;
std::srand(std::time(0));
std::shared_ptr<Grand> pg = nullptr;
std::shared_ptr<Superb> ps = nullptr;
for (int i = 0; i < 5; i++)
{
pg = GetOne();
pg->Speak();
if (ps = std::dynamic_pointer_cast<Superb>(pg)) // 如果可以转换,则if条件判断为1,则执行ps->Say();反之,不执行
ps->Say();
}
cout << "const_pointer_cast***************************************************************" << endl;
int pop1 = 38383;
const int pop2 = 2000;
std::shared_ptr<int> po1(&pop1);
std::shared_ptr<const int> po2(&pop2);
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
int n = -103;
// 转换成功
std::shared_ptr<int> pc1 = nullptr;
pc1 = std::const_pointer_cast<int>(po1);
*pc1 += n;
// 转换失败
std::shared_ptr<int> pc2 = nullptr;
pc2 = std::const_pointer_cast<int>(po2);
*pc2 += n;
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
cout << "static_pointer_cast***************************************************************" << endl;
Grand x = Grand(10); // 基类对象
std::shared_ptr<Grand> w(&x); // 基类指针
Superb z = Superb(11); // 继承类对象
std::shared_ptr<Superb> pointer1(&z);
std::shared_ptr<Superb> y = std::static_pointer_cast<Superb>(w); // 将基类指针转换为继承类指针 允许
y->Speak(); // 只是允许但是不推荐,因为转换为继承类指针后可以调用继承类的成员方法,而基类没有该成员方法,会引发异常
std::shared_ptr<Grand> a = std::static_pointer_cast<Grand>(pointer1); // 将继承类指针转换为基类指针
a->Speak();
cout << "reinterpret_pointer_cast***************************************************************" << endl;
struct dat
{
short a;
short b;
};
// 这样转换其实是不安全的,程序员需要主动保证程序的安全性。
// 本程序运行结束程序的返回值非0,是错的
long value = 0xA224B118;
std::shared_ptr<long> data(&value);
std::shared_ptr<dat> pd = std::reinterpret_pointer_cast<dat>(data);
std::cout << std::hex << pd->a; // display first 2 bytes of value
// std::cout << std::hex << pd->b; // display first 2 bytes of value
return 0;
}
std::shared_ptr<Grand> GetOne() // generate one of three kinds of objects randomly
{
std::shared_ptr<Grand> p = nullptr;
switch (std::rand() % 3)
{
case 0:
p.reset(new Grand(std::rand() % 100));
break;
case 1:
p.reset(new Superb(std::rand() % 100));
break;
case 2:
p.reset(new Magnificent(std::rand() % 100,
'A' + std::rand() % 26));
break;
}
return p;
}
运行结果:
C:\Users\15495\Documents\Jasmine\Work\coding\cmake-build-debug\coding.exe
I hold the character O and the integer 66!
I am a superb class!!
I hold the superb value of 49!
I am a magnificent class!!!
I hold the character V and the integer 33!
const_pointer_cast***************************************************************
pop1, pop2: 38383, 2000
pop1, pop2: 38280, 2000
static_pointer_cast***************************************************************
I am a grand class!
I am a superb class!!
reinterpret_pointer_cast***************************************************************
b118
Process finished with exit code -1073740940 (0xC0000374)