MST

星途 面试题库

面试题:C++ 构造函数非虚声明与对象创建过程的关系

在C++中,详细说明构造函数非虚声明对对象创建过程的影响,包括内存分配、基类和派生类构造函数的调用顺序等方面,同时分析若将构造函数声明为虚函数会带来哪些潜在问题。
30.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 构造函数非虚声明对对象创建过程的影响
    • 内存分配
      • 当创建一个对象时,首先会为该对象分配内存。对于包含继承关系的类层次结构,内存分配遵循一定规则。系统会根据对象的类型(包括基类和派生类部分),在堆(如果使用new动态分配)或栈(如果是局部对象)上分配足够的空间。例如,如果有一个基类Base和一个派生类Derived继承自Base,创建Derived对象时,会为Base部分和Derived新增的成员变量分配连续的内存空间。
    • 基类和派生类构造函数的调用顺序
      • 先调用基类的构造函数,然后再调用派生类的构造函数。这是因为在派生类对象构造时,其基类部分必须先被初始化。例如:
class Base {
public:
    Base() {
        std::cout << "Base constructor" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        std::cout << "Derived constructor" << std::endl;
    }
};

int main() {
    Derived d;
    return 0;
}

在上述代码中,创建Derived对象d时,会先输出Base constructor,再输出Derived constructor

  1. 将构造函数声明为虚函数带来的潜在问题
    • 语义问题:虚函数机制是基于对象的动态类型来决定调用哪个函数版本,而在对象构造期间,对象还未完全形成,其动态类型是不完整的。构造函数的主要目的是初始化对象的成员变量,将其声明为虚函数与构造函数的基本职责相冲突。
    • 性能问题:虚函数调用需要通过虚函数表(vtable)来实现,这会增加额外的开销。对于构造函数这种在对象创建时频繁调用的函数,额外的虚函数表查找开销会降低程序的性能。
    • 实现问题:由于构造函数调用顺序的原因,在基类构造时,派生类部分还未初始化。如果基类构造函数是虚函数,并且在基类构造函数中调用了虚函数,那么实际调用的将是基类版本的函数,而不是派生类版本(即使最终创建的是派生类对象),这往往不符合预期的多态行为。例如:
class Base {
public:
    virtual void print() {
        std::cout << "Base print" << std::endl;
    }
    Base() {
        print();
    }
};

class Derived : public Base {
public:
    void print() override {
        std::cout << "Derived print" << std::endl;
    }
    Derived() {}
};

int main() {
    Derived d;
    return 0;
}

在上述代码中,Base构造函数中调用print函数,输出的是Base print,而不是Derived print,这与期望的多态行为不符。