第十三章 类继承2

虚函数与多态

虚函数对类的影响

class A
{
private:
    int a;
public:
    void func1() {};
    void func2() {};
}

cout << sizeof(A) << endl;

 上述计算结果是多少呢? 很显然, 计算结果的大小是4, 在类中类的大小由成员变量决定的, 成员函数不占用类大小. 成员变量与成员函数是分开存储的.

class A
{
private:
    int a;
public:
   virtual void func1() {};
   virtual void func2() {};
}

cout << sizeof(A) << endl;

现在的结果是多少呢? 编译器结果输出是16, 其中int 占4字节.为什么会多出12字节呢? 答案很简单,当一个类中加入虚函数时,此时的成员变量会增加一个名字叫vptr的指针.由于系统是64位,所以指针大小是8字节,考虑内存对齐的原因,所以结果是16字节.

虚函数表的生成时机和原因

在编译期间,如果类中的虚函数>=1的时候,编译器在编译阶段会为类生成一个虚函数表vtbl.生成虚函数表的原因是为了实现动态绑定或运行时多态性.

虚函数表指针被赋值的时机

在编译期间, 编译器会向类的构造函数中插入一条为虚函数表指针赋值的语句,将虚函数表的地址赋值给vptr.

多态的概念

C++中的多态是指通过基类的指针或引用调用派生类的成员函数时,根据实际对象的类型来确定调用的是哪个类的成员函数。即方法的行为取决于调用该方法的对象.

构造函数与虚析构函数

构造函数为什么不能是虚函数呢?

第一点: 虚函数的调用需要用到虚函数表指针,在上面的介绍中,我们知道了虚函数表指针生成和赋值的时机,在创建对象时,编译器会为对象分配内存,之后调用构造函数进行初始化工作.如果构造函数是虚函数,那么调用构造函数的时候需要用到虚表指针指向的虚函数表,由于构造函数是虚函数,所以现在的虚函数表指针的指向是不可知的,就无法调用构造函数.所以构造函数不能是虚函数.

第二点:静态联编在编译期间能够知道使用的函数,由于虚函数的出现,使得静态联编变得困难,就是说在编译期间无法确定要使用的函数,所以需要动态联编.而对于构造函数,编译器能明确知道调用的对象,所以将构造函数声明为虚函数完全没有意义.

析构函数为什么要成为虚函数

使用虚析构函数能够让正确的析构函数序列被调用 

友元函数与重新定义 

友元函数是否能为虚函数

友元函数不能为虚函数, 因为友元函数不是类成员,而只有类成员才能是虚函数

派生类函数重定义

没有重新定义

如果派生类函数没有重新定义函数, 则派生类将使用原来函数的基类版本.

重新定义将隐藏方法

class A
{
public:
    virtual void func1(int x) const;
}

class B : public A
{
public:
    virtual void func1() const;
}

B b;
b.func1()    // ok
b.func11(6) // no

 新的函数定义将隐藏基类的的同名函数.所以这引出了两条经验规则

第一: 如果重新定义继承的方法, 应该确保与基类的原型完全相同.但如果返回类型是基类引用或者指针, 则可以修改为指向派生类的引用或指针.

第二:如果基类声明被重载了, 则应在派生类中重新定义所有的基类版本

class A
{
public:
    virtual void func1(int x) const;
    virtual void func1(double x) const;
    virtual void func(() const;
}

class B : public A
{
public:
    virtual void func1() const;
    virtual void func1(double x) const;
    virtual void func(() const;
}

 如果只定义一个版本,其他的两个版本将被隐藏.派生类对象将无法使用他们.

你可能感兴趣的:(c++,primer,plus,c++)