MST

星途 面试题库

面试题:C++ 构造函数非虚声明的特性及应用场景

请阐述为什么C++构造函数通常不声明为虚函数,以及在哪些实际应用场景中,构造函数非虚声明的设计模式能起到关键作用,并举例说明。
33.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 为什么C++构造函数通常不声明为虚函数

  • 对象创建过程: 构造函数用于创建对象并初始化其成员变量。在对象创建时,虚函数表指针(vptr)的设置依赖于对象的完整构造。若构造函数为虚函数,在对象尚未完全构造好时,就需要确定虚函数表指针,这会导致逻辑混乱。因为虚函数表指针需要基于完整构造的对象来确定其指向,若构造函数是虚函数,对象还在构建过程中,无法正确设置虚函数表指针,进而无法正确调用虚函数。
  • 性能和开销: 虚函数调用涉及通过虚函数表进行间接寻址,这会带来额外的性能开销。而构造函数通常是对象创建时调用一次,额外的虚函数调用开销对于构造函数而言是不必要的,会增加程序的负担。
  • 动态绑定机制: 虚函数实现的是动态绑定,其依据对象的实际类型在运行时确定调用的函数版本。但在构造函数执行期间,对象的类型已经确定,不需要动态绑定。构造函数负责将对象从无到有构建起来,在此过程中无需根据对象的实际类型在运行时选择不同的构造方式。

2. 实际应用场景及举例

  • 单例模式
    • 场景描述:在单例模式中,确保一个类只有一个实例,并提供全局访问点。构造函数非虚声明至关重要,因为单例模式下,类的实例化过程是固定且特定的,不希望有任何基于动态类型的变化。
    • 示例代码
class Singleton {
private:
    static Singleton* instance;
    Singleton() {} // 构造函数非虚且私有,防止外部实例化
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;
  • 工厂模式
    • 场景描述:工厂模式用于创建对象,将对象的创建和使用分离。工厂类负责根据不同条件创建不同类型的对象。在这种模式下,各个产品类的构造函数非虚,因为每个产品类的构造过程是明确且固定的,工厂根据条件选择合适的类进行实例化,而非通过动态绑定来调用构造函数。
    • 示例代码
class Product {
public:
    virtual void use() = 0;
};
class ConcreteProductA : public Product {
public:
    ConcreteProductA() {} // 构造函数非虚
    void use() override {
        std::cout << "Using ConcreteProductA" << std::endl;
    }
};
class ConcreteProductB : public Product {
public:
    ConcreteProductB() {} // 构造函数非虚
    void use() override {
        std::cout << "Using ConcreteProductB" << std::endl;
    }
};
class Factory {
public:
    static Product* createProduct(int type) {
        if (type == 1) {
            return new ConcreteProductA();
        } else if (type == 2) {
            return new ConcreteProductB();
        }
        return nullptr;
    }
};