通过转换基类指针来设置派生类字段


class A
{
public: 
    int a;
};
class B:public A
{
public:
    int b;
    void foo()
    {
        b=a*a;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{ 
    A * a=new A;
    a->a=10;
    ((B*)a)->foo();
    cout<<((B*)a)->b;
}

这是工作的b=100,但我不知道它的工作规则。b存储在哪里?我只是不知道怎么叫谷歌

基本上,这里发生的是未定义行为。它没有特殊的名字;它很可能被称为编程错误。你的类A的内存布局是:

int a;

B的内存布局为:

int a;
int b;

因此,在您的示例中,您只为a分配空间,但是您很幸运,它之后的空间是空闲的(这样就不会覆盖其他信息),并且它不会覆盖未分配的空间(否则,在尝试向未分配的页面写入时可能会发生错误)。因此,b存储在空闲空间中。

简而言之:不要依赖这段代码来工作!

行为未定义。只有当是指向B的指针时,才能将a转换为B*

不要这样做。

你甚至不能写A* a = new B;后面跟着(dynamic_cast<B*>(a))->foo();,因为类不是多态类型

@anderas提供了一个很好的解释为什么行为是未定义的。

这是标准(n4431,重点是我的)中的一个相关条款:

11……

如果"指向cv1 B的指针"类型的右值指向实际上是D类型对象的子对象的B,则结果指针指向D类型的封闭对象。否则,强制转换的结果是未定义的

[expr.static.cast]

因此,代码中的强制转换是未定义的。

可以这样写:

class A
{
public: 
    int a;
    virtual void foo() = 0; // make it polymorphic
};
class B:public A
{
public:
    int b;
    virtual void foo()
    {
        b=a*a;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{ 
    A * a=new B;  // create an instance of B
    a->a=10;
    ((B*)a)->foo();
    cout<<((B*)a)->b;
    // don't forget to delete 'a'
}

你的代码将导致两个未定义的行为:

  1. 当你将A的实例转换为B时。
  2. 当您使用成员变量b时(该变量不存在)在内存中)。

下面是使用B的实例作为a的指针的潜在实现

class A
{
public:
    void setA(int aToSet)
    {
       a = aToSet;
    }
    virtual void foo() = 0;
    virtual void getResult() const = 0;
private:
    int a;
};
class B : public A
{
public:
    void foo() override
    {
        b = a * a;
    }
    void getResult() const override
    {
        return b;
    }
private:
    int b;
};
int _tmain(int argc, _TCHAR* argv[])
{ 
    A *a = new B();
    a->setA(10);
    a->foo();
    cout << a->getResult();
}

最新更新