您的位置:首页 > 编程语言 > C语言/C++

cast(转化)总结

2017-05-04 15:55 183 查看
转化其实是一种编译器指令。大部分情况下它并不改变一个指针所含的真正地址,它只影响“被指出之内存大小和其内容”的解释方式。因此一个类型为void*的指针只能够持有一个地址,而不能通过它操作所指之object。

不管怎么转化,如果从多次派生的类(而且某些派生类多重继承了某些基类)直接转化成基类都可能不安全(因为可能这个类对象里面有多个基类对象,不知道该转换成哪一个),所以上行转化的时候一步步的上行最好最安全,而且这种情况出现了本来就是错误,这样谁能说清楚想把派生类到底转化成哪一个基类。

static_cast:

1.用于类层次结构中,基类和子类之间转化:

1)子类类型的变量可以直接转化为父类的,而且转化的非常彻底,包括虚表,反之则不行(虚表也被转化的原因是:编译器在初始化及指定操作中间作出了仲裁。编译器必须确保如果一个object含有一个或一个以上的vptrs,那些vptrs的内容不会被base class object初始化或改变)。

2)当进行上行转化,也就是把子类的指针或引用转换为父类表示,这种转化是安全的,转化了除了虚表之外的其他所有东西,如非虚成员函数,所以应该可以实现多态。

3)当进行下行转化,也就是把父类的指针或引用转化为子类表示,这种转化不能转化虚表,其他的可以转化,这时这种转化就会有安全问题了,因为可能子类的成员变量比父类多,这是可能成员变量的初始化方面会出现问题。另一方面,转换之后地址并不会发生变化,那么显然如果子类的成员变量比父类多的话就有可能访问非法内存,不论是任何类型的空间。

/*************************************************************************
> File Name: static_cast.cpp
> Author:
> Mail:
> Created Time: Sat 29 Apr 2017 11:09:35 PM CST
************************************************************************/

#include<iostream>
using namespace std;

class Base {
public:
virtual void output() {
cout << "Base()" << endl;
}
void output2() {
cout << "Base's output2" << endl;
}
};

class Derived : public Base {
public:
Derived(int d[]) {
for(int i=0; i<12345; i++)
this->d[i] = d[i];
}
virtual void output() {
cout << "Derived()" << endl;
}
void output2() {
cout << "Derived's output2 " << d[0] << endl;
}
public:
int d[12345];
};
int temp[12345];
int main() {
int e=8;
Base b1;
Base *b2 = new Base();
int d=8;
int f[20];
f[0]=0;
Derived d1(temp);
Derived *d2 = new Derived(temp);
//(static_cast<Derived>(b1)).output();
(static_cast<Derived&>(b1)).output();
(static_cast<Derived *>(b2))->output();
(static_cast<Base>(d1)).output();
(static_cast<Base&>(d1)).output();
(static_cast<Base *>(d2))->output();
//(static_cast<Derived>(b1)).output2();
cout << "d: " << d << endl;
(static_cast<Derived&>(b1)).output2();
cout << &b1 << " " << &(static_cast<Derived&>(b1)) << endl;
cout << (static_cast<Derived&>(b1)).d << endl;
/*for(int i=0; i<12345; i++)   //这个地方会大量使用不属于b1的内存,从而导致出现很大很大的问题。
(static_cast<Derived&>(b1)).d[i]=121233;*/
cout << "d: " << d << endl;
cout << "&d: " << &d << endl;
cout << "e: " << e << endl;
cout << "&e: " << &e << endl;
cout << "&b1: " << &b1 << endl;
cout << "f:" << f << endl;
cout << "f[0]: " << f[0] << endl;
(static_cast<Derived *>(b2))->output2();
cout << b2 << " " << (static_cast<Derived *>(b2)) << endl;
(static_cast<Base>(d1)).output2();
cout << &d1 << " " << &(static_cast<Derived&>(d1)) << endl;
(static_cast<Base&>(d1)).output2();
(static_cast<Base *>(d2))->output2();
}
本机运行结果:
Base()
Base()
Base()
Derived()
Derived()
d: 8
Derived's output2 0
0x7ffce4d6e0a0 0x7ffce4d6e0a0
0x7ffce4d6e0a8
d: 8
&d: 0x7ffce4d6e09c
e: 8
&e: 0x7ffce4d6e098
&b1: 0x7ffce4d6e0a0
f:0x7ffce4d6e0d0
f[0]: 0
Derived's output2 0
0xa0dc20 0xa0dc20
Base's output2
0x7ffce4d6e120 0x7ffce4d6e120
0x7ffce4d6e0b0 0x7ffce4d6e0b0
Base's output2
Base's output2


dynamic_cast(run-time operation, 成本较高):

1.用于类层次结构中,基类和子类之间转化:

1)两个类型的变量之间不能互相转化

2)当进行上行转化,也就是把子类的指针或引用转换为父类表示,这种转化是安全的,转化了除了虚表之外的其他所有东西,如非虚成员函数,所以应该可以实现多态。

3)当进行下行转化的时候,可以说dynamic帮你保证了安全性,如果基类指针本来指向的就是派生类,那么就可以安全的转化,否则转化过程中直接就会报错(如果是指针转化出错,那么返回的指针值为NULL,如果是引用转化出错,会直接抛出异常)。

/*************************************************************************
> File Name: dynamic_cast.cpp
> Author:
> Mail:
> Created Time: Sun 30 Apr 2017 10:15:59 AM CST
************************************************************************/

#include <iostream>
using namespace std;

class A {
public:
A() {}
virtual void test() {
cout << "test: A" << endl;
}
void test2() {
cout << "test2: A" << endl;
}
};
class B : public A {
public:
B() : A() {}
virtual void test() {
cout << "test: B" << endl;
}
void test2() {
cout << "test2: B" << endl;
}
};
class C : public B {
public:
C() : B() {}
virtual void test() {
cout << "test: C" << endl;
}
void test2() {
cout << "test2: C" << endl;
}
};
class D {
// ...
};
void test(void *aim) {
A *a = static_cast<A *>(aim);
D *d = static_cast<D *>(aim);
}
int main() {
C *pc = new C;
B *pb = dynamic_cast<B *>(pc);
A *pa = dynamic_cast<A *>(pc);
cout << "pa:" << pa << " pb:" << pb << " pc:" << pc << endl;
A *a = new A;
D *d = new D;
dynamic_cast<void *>(a);//但是dynamic_cast没法转化回去,想转化回去得靠static_cast
//dynamic_cast<void *>(d);不可以把D类型指针转化成void类型,因为D类型没有多态性(没有虚函数),所以认为没有必要转化它的类型
//C c;
//B b;
//dynamic_cast<B>(c);无论上行转化还是下行转化都不行
//dynamic_cast<C>(b);

B *b = new C;
B *b2 = new B;
C *c = dynamic_cast<C*>(b);
C *c2 = dynamic_cast<C*>(b2); //b2本来不是指向C类型的指针,所以会转化失败,此时c2为NULL,所以显然dynamic_cast相对于static_cast下行转化时更安全。
cout << "b:" << b << " b2: " << b2 << " c: " << c << "  c2: " << c2 << endl;
b->test();
c->test();
b2->test();
}
运行结果如下:
pa:0x2276c20 pb:0x2276c20 pc:0x2276c20
b:0x2277090 b2: 0x22770b0 c: 0x2277090  c2: 0
test: C
test: C
test: B


const_cast:

const_cast用来将类型的const、volatile和__unaligned属性移除。常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。但是直接定义的常量是无法改变的,即使通过const_cast转化除了非常量类型的引用也不行,所以可以看出来const_cast只是为了让常量对应的那一块内存可以修改,而不是为了修改常量(但也有说法说其实修改一个常量对应的地址空间的值是未定义的行为,最后发现所谓的无法改变只是因为整形常量被编译器优化不从地址取值,而是直接记住值,加一个volatile修饰就会发现也是会改变的,后面的博客还有提到)。

/*************************************************************************
> File Name: const_cast.cpp
> Author: canispeakchinese
> Mail: canispeakchinese@qq.com
> Created Time: Sun 30 Apr 2017 10:59:45 AM CST
************************************************************************/

#include <iostream>
using namespace std;
class CA {
public:
CA() : m_iA(10) {}
int m_iA;
};
int main() {
const CA *pA = new CA;
CA *pB = const_cast<CA *>(pA);
pB->m_iA = 100;
cout << pA->m_iA << endl;
cout << pB->m_iA << endl;
//pA->m_iA = 1000;pA指针并没有转化
const CA &a = *pA;
CA &b = const_cast<CA &>(a);
b.m_iA = 200;
cout << b.m_iA << endl;
cout << pA->m_iA << endl;
const CA a2;
CA &b2 = const_cast<CA&>(a2);
cout << &a2 << " " << &b2 << endl;
b2.m_iA=300;
cout << a2.m_iA << " " << b2.m_iA << endl;
const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
cout << constant << " " << *const_p << " " << *modifier << endl;
int& modifier2 = const_cast<int&>(constant);
modifier2 = 8;
cout << constant << " " << *const_p << " " << *modifier << " " << modifier2 << endl;
cout << &constant << " " << const_p << " " << modifier << " " << &modifier2 << endl;
return 0;
}
运行结果如下:
100
100
200
200
0x7ffc6bc33650 0x7ffc6bc33650
300 300
21 7 7
21 8 8 8
0x7ffc6bc33654 0x7ffc6bc33654 0x7ffc6bc33654 0x7ffc6bc33654


reinterpret_cast:

可以将任意一种类型转化成另一种类型。reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的(这句话是C++编程思想中的原话)。reinterpret_cast仅仅是复制比特位,没有进行必要的分析。比如用reinterpret_cast将int转化为double之后,double的值也是不对的。而且reinterpret_cast也有访问非法内存的危险。

/*************************************************************************
> File Name: reinterpret_cast.cpp
> Author: canispeakchinese
> Mail: canispeakchinese@qq.com
> Created Time: Sun 30 Apr 2017 07:56:46 PM CST
************************************************************************/

#include <iostream>
using namespace std;
struct p {
int a[20];
};
int main() {
int *a = new int[5];
int *n = new int(123);
int *c = new int[5];
p *d = reinterpret_cast<p *>(n);
cout << sizeof(*n) << " " << sizeof(*d) << endl;
cout << n << " " << d << " " << &(d->a[0]) << " " << &static_cast<int &>(*n)
<< " " << &reinterpret_cast<long long&>(*n) << endl;
c[0] = 0;
for(int i=0; i<20; i++)
d->a[i] = 123456;
cout << a << " " << n << " " << c << endl;
cout << *n << " " << d->a[0] << " " << a[0] << " " << c[0] << endl;
return 0;
}
运行结果如下:
4 80
0x1b74c40 0x1b74c40 0x1b74c40 0x1b74c40 0x1b74c40
0x1b74c20 0x1b74c40 0x1b74c60
123456 123456 0 123456
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 类型转化