关于C++对象模型的经典问题

关于C++ 的继承 多态 和与之相关的对象模型知识,各个书本上很多了
今天来看几个对象模型的问题 在实践中理解C++ 对象模型:

1、 在下列代码中:

class A
{
public :
    A()
        :_a(1)
    {}
    virtual void FunTestA()
    {}
    int _a;
};

class B
{
public:
    B()
        :_b(2)
    {}
    virtual void FuntestB()
    {}
    int _b;
};

class C :public A, public B
{
public :
    C()
    :A()
    , B()
    , _c(3)
    {}
    int _c;
};

void Fun()
{
    C c;
    cout << sizeof(c) << endl;
    A* pA = &c;
    B* pB = &c;
    C* pC = &c;
}

`
nt main()
{
    Fun();
    return 0;
}

选项:
A pA、pB、pC的取值相同
B、 pC 和 pA不相同
C、pB和pC不相同
D、pC不等于pA,也不等于pB。

解析:在继承体系中,当用一个基类的指针或引用指向派生类对象时(赋值兼容规则),这时会将派生类对象的地址偏移一个合适偏移量,因为派生类对象也是一个基类对象,实际也就是让基类指针指向派生类中属于基类的那部分。所以一般先继承的那个类(基类)的指针和派生类的对象的地址相等。

这里写图片描述

2、 类A 与类 B毫无关系

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    void fun()
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B
{
public :
    B()
    {
        m_c = 3;
    }

    void fun()
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B* pb = (B*)(&a);
    pb->fun();
}

int main()
{
    Fun();
    return 0;
}

A 类与 B 类毫无关系,则pd->fun();一定到用的是派生类的fun()函数,
但是pb又是被强制指向A类对象的,
那么肯定会打印前A类对象前四个字节的值,也就是1。(因为B类大小就是4个字节)。因为B类的大小的4个字节,所以B类的指针管理的内存范围是4个字节。

这里写图片描述

3、 当B类共有继承A类的时候

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    void fun()
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B:public A
{
public :
    B()
        :A()
    {
        m_c = 3;
    }

    void fun()
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B* pb = (B*)(&a);//切记:这里是派生类的指针指向基类对象
    //没有定义基类。
    pb->fun();
}

int main()
{
    Fun();
    return 0;
}

这里pd->fun()还是调用的派生类的fun函数,可这时派生类继承了基类,所以派生类的指针管理的范围就是 (4 + 8) = 12个字节。而继承基类A的派生类B 并没有被定义,m_c的空间还没有开辟出来,所以这时候打印m_c结过时崩溃或随机数。

这里写图片描述

4、当A类的fun函数被声明为虚函数时,并且定义了B类时。

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    virtual void fun()//虚函数
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B:public A
{
public :
    B()
        :A()
    {
        m_c = 3;
    }

    void fun()//重写
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B b;//注意这里定义了B类
    B* pb = (B*)(&a);//切记:这里是派生类的指针指向基类对象,不会形成多态
    pb->fun();//在A类的虚表内找fun函数(调用的是A类的fun函数),输出1和2
    A* pa = (A*) &b;
    pa->fun();//这里形成了多态 打印 。
}

这里虽然定义了B类,但是没有形成多态,pb实际上指向的是A的空间pd->fun()是在A的虚表中找vptr 函数指针 所以调用的是A的fun()函数。

4、 如果是基类fun函数是普通函数,派生类fun()函数是虚函数

class A
{
public:
    A()
    {
        m_a = 1;
        m_b = 2;
    }

     void fun()
    {
        cout << m_a << "  " << m_b << endl;
    }
private:
    int m_a;
    int m_b;
};

class B :public A
{
public:
    B()
        :A()
    {
        m_c = 3;
    }

    virtual void fun()
    {
        cout << m_c << endl;
    }

private:
    int m_c;
};

void Fun()
{
    A a;
    B b;
    B* pb = (B*)(&a);//切记:这里是派生类的指针指向基类对象
    pb->fun();
    A* pa = (A*) &b;
    pa->fun();
}

int main()
{
    Fun();
    system("pause");
    return 0;
}

这时候仍然没有形成多态, 但是B类的fun()函数是虚函数,pb指向&a.所以pb->fun()这一句,会在 A对象的前四个字节中寻找虚表,所以他把m_a当成虚表使得程序崩溃。

这里是因为没有形成多态,所以pb虽然指向&a但是pb的类型是B* pb会按照B类的结构来解释在A类内存空间中的数据。

这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章