单例继承疑问:调用派生类构造时创建的是哪个对象?
单例继承场景下的实例类型疑问
我创建了两个类——基类和派生类,其中基类是Singleton(单例)。当调用派生类的构造函数时,作为静态实例被创建的是哪个对象?是基类对象还是派生类对象,原因是什么?
代码示例
#include <iostream> class Singleton { protected: static Singleton* instance; Singleton() {} public: static Singleton* getInstance() { if (!instance) { instance = new Singleton(); } return instance; } }; Singleton* Singleton::instance= nullptr;; class DerivedSingleton : public Singleton { public: int x; static DerivedSingleton* getInstance() { if (!instance) { instance = new DerivedSingleton(); } return (DerivedSingleton*)instance; } }; int main(){ Singleton *instance = DerivedSingleton::getInstance(); std::cout << sizeof(DerivedSingleton) << std::endl; std::cout << sizeof(*instance) << std::endl; std::cout << sizeof(Singleton) << std::endl; }
输出结果
4 1 1
我尝试打印了大小值,但无法理解为何创建的是基类对象。
解答
首先明确:实际创建的是DerivedSingleton对象,不是基类对象,你看到的sizeof结果是编译期特性导致的误解,具体原因如下:
- 静态成员的共享性:基类
Singleton的静态成员instance是所有派生类共享的。第一次调用DerivedSingleton::getInstance()时,instance为空,所以会执行new DerivedSingleton(),此时分配的是派生类的内存(包含基类部分和int x成员,所以sizeof(DerivedSingleton)是4)。 - sizeof的编译期计算:
sizeof(*instance)的结果由instance的静态类型(Singleton*)决定,这是编译阶段就确定的。空类Singleton的大小是1(C++标准要求每个对象必须有唯一地址,空类会被分配1字节的占位空间),所以不管instance运行时实际指向什么对象,sizeof(*instance)都会返回1。 - 验证实际对象类型:如果在main函数里把指针转换为派生类类型,就能正常访问派生类的成员,证明实际是派生类对象:
DerivedSingleton* d_inst = static_cast<DerivedSingleton*>(instance); d_inst->x = 100; std::cout << d_inst->x << std::endl; // 输出100,说明对象确实是DerivedSingleton类型 - 当前实现的隐患:这种单例继承写法有严重问题——如果先调用
Singleton::getInstance(),instance会被初始化为基类对象,之后DerivedSingleton::getInstance()会强制转换这个基类指针为派生类指针,此时访问x会触发未定义行为。正确的单例继承应该让派生类拥有自己的静态实例成员,避免共享基类的静态变量。
内容的提问来源于stack exchange,提问作者Gaurav Mehta
相关产品推荐
相关产品推荐

