一个关于基类和派生类指针的有关问题

一个关于基类和派生类指针的问题
直接上程序吧:
C/C++ code

#include <iostream>
using namespace std;

//没有使用虚函数的继承派生关系
class Base
{
public:
    Base(int i = 0):ival(i){}
    void getVal()
    {
        cout<<ival<<endl;
    }
private:
    int ival;
};

class Derived:public Base
{
public:
    Derived(int i = 0, int j = 1):Base(i),ival(j){}
    void getVal()
    {
        cout<<ival<<endl;
    }
private:
    int ival;
};

//使用了虚函数的继承派生关系
class Base1
{
public:
    Base1(int i = 0):ival(i){}
    virtual void getVal()
    {
        cout<<ival<<endl;
    }
private:
    int ival;
};

class Derived1:public Base1
{
public:
    Derived1(int i = 0, int j = 1):Base1(i),ival(j){}
    void getVal()
    {
        cout<<ival<<endl;
    }
private:
    int ival;
};


void useBaseObj(Base b)
{
    b.getVal();
}

void useDerivedObj(Derived d)
{
    d.getVal();
}

void useBasePtr(Base *pb)
{
    pb->getVal();
}

void useDerivedPtr(Derived *pd)
{
    pd->getVal();
}

void useBase1Obj(Base1 b)
{
    b.getVal();
}

void useDerived1Obj(Derived1 d)
{
    d.getVal();
}

void useBase1Ptr(Base1 *pb)
{
    pb->getVal();
}

void useDerived1Ptr(Derived1 *pd)
{
    pd->getVal();
}

int main()
{
    Base b;
    Derived d;

    Base *pb = &b;
    Derived *pd = &d;
    useBaseObj(b);//基类实参,基类形参,调用基类函数
    useDerivedObj(d);//派生类实参,基类形参,自动转化,调用基类函数
//    useDerivedObj((Derived)b);//无法用基类(自动的)构造出一个派生类
    useDerivedObj(d);//派生类实参,派生类形参,调用派生类函数
    cout<<endl;

    useBasePtr(pb);//指向基类形参,指向基类实参,调用基类函数
    useBasePtr(pd);//指向基类形参,指向派生类实参,调用静态类型-基类函数
//    useDerivedPtr(pb);//指向基类实参,指向派生类形参,无法自动转化
    useDerivedPtr((Derived*)pb);//强制类型转化,打印结果为随机数
                                //为什么?
    useDerivedPtr(pd);//指向派生类实参,指向派生类形参,调用派生类
    cout<<endl;

    pb = new Derived;//静态类型为指向基类,动态类型为指向派生类
    useBasePtr(pb);//调用基类
    useDerivedPtr((Derived*)pb);//调用派生类
    cout<<endl;

    Base1 b1;
    Derived1 d1;
    useBase1Obj(b1);//基类实参-基类形参,调用基类函数
    useBase1Obj(d1);//派生类实参-基类形参,发生派生类到基类的转化,调用基类
//    useDerivedObj((Derived)b);//基类实参,派生类形参,无法类型转化,函数报错
    useDerived1Obj(d1);//派生类实参-派生类形参,调用派生类函数
    cout<<endl;

    Base1 *pb1 = &b1;
    Derived1 *pd1 = &d1;
    useBase1Ptr(pb1);//指向基类的实参,指向基类的形参,调用基类函数
    useBase1Ptr(pd1);//指向派生类实参,指向基类的形参,根据多态,调用派生类
//    useDerivedPtr(pb1);//指向基类的实参,指向派生类形参,不匹配
    useDerived1Ptr((Derived1*)pb1);//通过强制类型转换使其匹配,调用基类函数
                                    //为什么?


    useDerived1Ptr(pd1);//指向派生类的实参,指向派生类的形参,调用派生类
    cout<<endl;

    pb1 = new Derived1;//形参静态类型为基类,动态类型为派生类
    useBase1Ptr(pb1);//根据多态,调用派生类
    useDerived1Ptr((Derived1*)pb1);//直接调用派生类
    return 0;
}



程序中的注释部分有两处不明白,请指教?
PS:其他的注释部分应该都没啥问题吧?

------解决方案--------------------
第一个问题的答案:
当你将pb强制转换为Derived类指针,并输出,实际上,UseDerivedPtr会认为传进来的就是一个Derived类型的对象的指针。所以,它会试图输出Derived对象的ival值。而对于一个Derived对象来说,它应该包含两个int成员,一个是基类的,而另一个是Derived自身定义的。所以,Derived对象的大小(size)应该是8字节,而Base对象只有4字节。
当你定义Base对象时,它自身包含的4字节以外的内容对它来说都是无意义的。所以,你把它的指针传给UserDerivedPtr,则后者会输出这个无意义的值,即随机数。

第二个问题的答案:
这个问题就需要用编译器实现的理论来回答了。对于没有虚函数的派生来说,编译器所做的中间代码转换是非常简单的。即直接找到指针对应的原始类类型,然后取查找它的函数地址,并执行,例如:
Base b;
Derived d;
Base* pb = &b;
Derived* pd = &d;

下面的方法调用及其对应的编译器中间转换结果分别为:
b.getVal() --> Base::getVal(&b);
d.getVal() --> Derived::getVal(&d);
pb->getVal() --> Base::getVal(pb);
pd->getVal() --> Derived::getVal(pd);
(注意):
pd = (Derived*)pb;
pd->getVal() --> Derived::getVal(pd);
这也解释了第一个问题的现象。

之所以这么翻译,是因为对于没有虚函数的类而言,所有的调用都是可以在编译器确定的。编译器看见对象、指针的类型,就能确定需要调用的方法到底是哪一个。

而如果有虚函数,情况就完全不一样。只要类拥有虚函数,则在对象的内存布局中,就会隐含添加一个虚函数表指针(vtable),这个函数表指针指向一个数组,该数组每一个元素对应该类所拥有的虚函数地址。

编译器在对这种情况做中间翻译时,就不一样了:
Base1 b1;
Derived1 d1;