在c风格的类型转换过程中,关于转换对象的地址空间实际会发生什么? 根据Cline的C++ C++FAQ,下面的代码描述了继承不当。 请注意,我可以在一袋苹果中加一根香蕉,香蕉是一种水果,实际上应该只含苹果。。但是由于继承关系,香蕉被添加到bagofApples中
但问题是在这条线上到底发生了什么:在c风格的类型转换过程中,关于转换对象的地址空间实际会发生什么? 根据Cline的C++ C++FAQ,下面的代码描述了继承不当。 请注意,我可以在一袋苹果中加一根香蕉,香蕉是一种水果,实际上应该只含苹果。。但是由于继承关系,香蕉被添加到bagofApples中,c++,inheritance,casting,C++,Inheritance,Casting,但问题是在这条线上到底发生了什么: { return (Apple&) BagOfFruit::remove(); }//what does happen here? c型铸造在这里做什么? 请注意,香蕉的大小是4004,而苹果的大小只有4。 因此,尽管苹果对象访问香蕉对象的remove函数,因为苹果函数的地址偏移量将与香蕉的地址偏移量匹配,但当香蕉被类型化为苹果对象时,香蕉的数据成员ar[1000]会发生什么情况? 苹果在内存中的位置&这里的引用,浇铸香蕉对象(实际对象)的地址空间
{ return (Apple&) BagOfFruit::remove(); }//what does happen here?
c型铸造在这里做什么? 请注意,香蕉的大小是4004,而苹果的大小只有4。 因此,尽管苹果对象访问香蕉对象的remove函数,因为苹果函数的地址偏移量将与香蕉的地址偏移量匹配,但当香蕉被类型化为苹果对象时,香蕉的数据成员ar[1000]会发生什么情况? 苹果在内存中的位置&这里的引用,浇铸香蕉对象(实际对象)的地址空间会发生什么变化 完整代码如下
#include "stdafx.h"
#include <iostream>
using namespace std;
class Fruit
{
public:
virtual void printClassName() const throw() = 0;
virtual ~Fruit() throw();
};
Fruit::~Fruit() throw()
{ }
class Apple : public Fruit
{
public:
virtual void printClassName() const throw();
};
void Apple::printClassName() const throw()
{ cout << "Apple\n"; }
class Banana : public Fruit
{
public:
virtual void printClassName() const throw();
protected:
int ar[1000];
};
void Banana::printClassName() const throw()
{ cout << "Banana\n"; }
//The following BagOfFruit class allows insertion and removal of objects of any
//kind-of Fruit.
class Full { };
class Empty { };
class BagOfFruit
{
public:
BagOfFruit() throw();
unsigned size() const throw();
void insert(Fruit& f) throw(Full);
Fruit& remove() throw(Empty);
protected:
enum { maxSize_ = 20 };
unsigned size_;
Fruit* data_[maxSize_];
};
BagOfFruit::BagOfFruit() throw()
: size_(0)
{ }
unsigned BagOfFruit::size() const throw()
{ return size_; }
void BagOfFruit::insert(Fruit& f) throw(Full)
{
if (size_ == maxSize_) throw Full();
data_[size_++] = &f;
}
Fruit& BagOfFruit::remove() throw(Empty)
{
if (size_ == 0) throw Empty();
return *data_[--size_];
}
void insertFruitIntoBag(BagOfFruit& bag, Fruit& fruit)
{
bag.insert(fruit);
}
class BagOfApple : public BagOfFruit {
public:
BagOfApple() throw();
void insert(Apple& a) throw(Full);
Apple& remove() throw(Empty);
};
BagOfApple::BagOfApple() throw()
: BagOfFruit()
{ }
void BagOfApple::insert(Apple& a) throw(Full)
{ BagOfFruit::insert(a); }
Apple& BagOfApple::remove() throw(Empty)
{ return (Apple&) BagOfFruit::remove(); }//what does happen here?
int _tmain(int argc, _TCHAR* argv[])
{
BagOfApple bagOfApple;
Banana banana;
insertFruitIntoBag(bagOfApple, banana);
cout << "Removing an Apple from bagOfApple: ";
Apple& a2 = bagOfApple.remove();
a2.printClassName();
return 0;
}
#包括“stdafx.h”
#包括
使用名称空间std;
等级水果
{
公众:
虚空printClassName()常量throw()=0;
虚拟~Fruit()投掷();
};
水果::~Fruit()扔()
{ }
苹果类:公众水果
{
公众:
虚空printClassName()常量throw();
};
void Apple::printClassName()常量throw()
{cout
c型铸造的作用是什么
这里
它没有任何作用。我的意思是,你只需要将引用类型更改为你的具体对象。如果你做了错误的转换,就会出现一些问题(即使编译器没有对此抱怨)。
您应该避免C++中的C样式转换。如果您想将基础对象引用转换为派生类引用,则需要在运行时检查该类型。请看。这是一个很好的教程。
因此,尽管香蕉对象的remove函数由苹果对象访问,因为苹果函数的地址偏移量将与香蕉对象的地址偏移量匹配
没有
苹果在内存中的位置&这里的引用,浇铸香蕉对象(实际对象)的地址空间会发生什么变化
<>没有。在这个例子中,Cases不会改变任何东西,因为它是一个引用类型,所以它创建了一个引用。他们只是告诉编译器不要抱怨类型。如果你需要C++中的C风格的演员,你可能做了一些错误。 这里的C风格类型的演员是做什么的?
编译器,它最终会帮助您
C风格强制转换的意义取决于编译器当时知道的内容
如果编译器已经看到
香蕉
,并且知道它是从水果
继承的,那么它就是
如果编译器对关系一无所知,则执行静态转换
在这两者之间,它是一个重新解释\u cast
结果(假设实际类型为Apple
)未定义
当您显式强制转换时,编译器假定您知道
您正在做的是,它获取对象的地址,并处理
在该地址的内存,就好像它是一个香蕉一样
香蕉
,一切都很好;如果不是,那你就是在骗警察
编译器,它会回来困扰你。大小不同
这只是一个明显的例子,如果你写的东西超出了
在Apple
的末尾,您将覆盖不属于您的内存
对象,或可能触发访问冲突。但即使
Apple
和Banana
大小相同,您的行为未定义
着陆后,几乎任何事情都可能发生。首先,如果此代码来自本书,则获取另一本书:
- 我理解这说明了一个故意的错误,即公开从一袋水果中衍生出一袋苹果(这是荒谬的,因为公开衍生意味着人们可以继续将一袋苹果作为一袋水果使用,从而在其中插入不是苹果的对象)但是
- 它充满了其他问题和糟糕的设计:
- 水果的所有权从来不是由袋子容器取得的……它们接受对对象的引用,然后获取它们的地址。通过没有
insert()
获取指针,它们错误地暗示对象是按值复制的。此用户界面容易因错误使用而导致应用程序崩溃
- 斯特劳斯特鲁普自己说,例外规范已经证明是一个错误,不应该使用(如果有人真的感兴趣并且自己找不到,我会尝试在网上找到采访记录的链接)
remove()
方法返回的值具有欺骗性。Comp Sci文献通常使用术语pop()
但问题是在这条线上到底发生了什么:
{ return (Apple&) BagOfFruit::remove(); }//what does happen here?
嗯,remove()
“pops”一个来自袋子/容器的水果,C风格的cast简单地向编译器保证,弹出的水果是一个苹果
。只有当袋子专门通过BagOfApples特定接口使用时,这才可能是正确的,但鉴于它公开派生自Bagofruit,某些代码完全可以将其用作Bagofruit并在其中插入一些其他类型的水果。如果返回的是苹果&
,但对象不是苹果
,并且有人试图操作假定的苹果
,则您将有未定义的行为
实际上,这段代码可能会像大多数实际编译器实现所期望的那样工作成员来存储它生长的区域。假设您去打印或比较区域,但对象实际上是一个香蕉:编译器可能会使用ar[0]
的值重新解释位,或者将ar[1]
(对于32位整数和64位系统)作为const char*
,
{ return (Apple&) BagOfFruit::remove(); }//what does happen here?