C++ 工程师面试体验(2)

3.谈谈这strcpy,memcpy和sprintf三者的区别?

共同点:都是拷贝东西。

效率排行: memcpy > strcpy > sprintf

操作对象:memcpy是两块内存之间的操作,strcpy 是两个字符串对象之间的操作,sprintf是任意类型转到字符串的操作。

4.C++多态机制

先上代码:

#include <iostream.h>
class animal
{
public:
    void sleep()
    {
        cout<<"animal sleep"<<endl;
    }
    void breathe()
    {
        cout<<"animal breathe"<<endl;
    }
};

class fish:public animal
{
public:
    void breathe()
    {
        cout<<"fish bubble"<<endl;
    }
};

int main()
{
    fish fh;
    animal *pAn=&fh;
    pAn->breathe();
    return 0;
}

运行结果是:

C++ 工程师面试体验

 

 

内存模式:

C++ 工程师面试体验

子类fish的内存模型是,先animal,然后再自己增加的部分。因为:

    animal *pAn=&fh;

fish对象被强制更改成父类animal对象,指针指向fish的上半部分(那不就是一个animal对象了吗?),所以pAn->breathe()就是父类的breathe()。

更深入的了解机制(早绑定和晚绑定):

 

早绑定:C++在编译的时候,要确定每个对象调用的函数的地址,这就是早绑定。

晚绑定:编译的时候先不决定,等到运行的时候,再根据情况确定要调用的函数地址,这就是晚绑定。

 

什么东西支持晚绑定这个机制呢?虚函数!

虚函数机制,每个类对象有个虚表指针:vptr,这个指针指向类所属的虚表:vtable。

 

当我们把父类animal的breathe()设置为虚函数,子类fish即使被强制转换成animal对象,也是没关系的。

因为pAn实际指向的就是fish对象!

因此vptr指向的是fish的vtable!

所以vptr指向的vtable中的breathe()函数就是fish对象的breathe()函数。

 

正确的多态代码:

 

#include <iostream.h>
class animal
{
public:
    void sleep()
    {
        cout<<"animal sleep"<<endl;
    }
    virtual void breathe()
    {
        cout<<"animal breathe"<<endl;
    }
};

class fish:public animal
{
public:
    void breathe()
    {
        cout<<"fish bubble"<<endl;
    }
};

int main()
{
    fish fh;
    animal *pAn=&fh;
    pAn->breathe();
    return 0;
}

运行结果:

C++ 工程师面试体验

 

再一次深入了解vptr和vtable:

 

每个含有virtual function的类中都隐式包含着:

一个静态虚指针vfptr指向该类的静态虚表vtable

 vtable中的表项指向类中的每个virtual function的入口地址

 

每个类内部都有一个虚表,无论类型怎么被转换,虚表指针vptr都是固定的。(fish被转换成animal也无所谓,vptr指向的永远是fish的vtable)


关于虚表:
1、每一个类都有虚表。

2、虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果基类3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。如果派生类有自己的虚函数,那么虚表中就会添加该项。
3、派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1a08bec43ddd6a6c35cd0aeab7392303.html