基类静态引用数据成员上的C++对象切片行为的奇怪案例



我遇到了一个奇怪的对象切片案例。我正在做一个项目,我需要单例类,所以我的基类和派生类都是单例的。下面的示例案例描述了我的情况。

这是我的基本类

// Base.h
class Base
{
    public:
        static Base& base;
        virtual void doSomething(){ cout<<"Base Do Something"<<endl; }
    protected:
        Base();
        virtual ~Base();
        static Base& getBaseInstance();
    private:
};
//Base.cpp
Base::Base()
{
    //ctor
}
Base::~Base()
{
    //dtor
}
Base& Base::getBaseInstance()
{
    static Base object;
    return object;
}
Base& Base::base=Base::getBaseInstance();

这是我的派生类

class Derived: public Base
{
    public:
    static Derived& derived;
    virtual void doSomething(){ cout<<"Derive Do Something"<<endl; }
  static Derived& getDerivedInstance();
    protected:
        Derived();
        virtual ~Derived();
    private:
};
Derived::Derived()
{
    //ctor
}
Derived::~Derived()
{
    //dtor
}
Derived& Derived::derived=Derived::getDerivedInstance();
Derived& Derived::getDerivedInstance()
{
    static Derived object;
    return object;
}

最后这是我的主要功能

int main()
{
    cout << "Hello world!" << endl;
    Base::base.doSomething();
    Derived::derived.doSomething();
    Base::base=Derived::derived;
    Base::base.doSomething();
    Base::base=Derived::getDerivedInstance();
    Base::base.doSomething();
    Base& r = Derived::derived;
    r.doSomething();
    Base::base=Derived::getDerivedInstance();
    Base::base.doSomething();
    return 0;
}

我得到了这个的以下输出

Hello world!
Base Do Something
Derive Do Something
Base Do Something
Base Do Something
Derive Do Something
Base Do Something

所以我的问题是,既然对象切片不应该对引用起作用,那么为什么我不能用派生对象覆盖我作为基类的静态数据成员创建的Base::base引用呢?虽然这在Base& r = Derived::derived; 上运行良好

我的意思是,当我用r.doSomething()调用do something时,我得到了doSomething of Derived类。但并非如此

    Base::base=Derived::derived;    
    Base::base.doSomething();

    Base::base=Derived::getDerivedInstance();    
    Base::base.doSomething();

任何形式的澄清都将不胜感激。非常感谢。

进行时

Base::base=Derived::derived;

您没有将基引用设置为引用派生类。这是赋值运算符,它所做的只是将derivedbaseBase部分赋值。

base仍然是Base类型,并且永远不会更改,因为引用只能初始化一次,并且您可以使用进行初始化

Base& Base::base=Base::getBaseInstance();

如果你想要这种重新分配行为,你需要使用指针类型。

Base::base是一个静态引用。你在这里初始化它:

Base& Base::base=Base::getBaseInstance();

从这一点开始(即在执行使用base的代码之前),base指的是基本实例(即在Base::getBaseInstance()中声明的静态实例)。

然后执行赋值时,将不再更改引用,而是将对象复制到base引用的对象中(该对象的类型为base,因此为切片!)。

一个解决方法可以是将Base::Base设置为指针。通过这种方式,您可以更改它并指向派生对象。然而,这是否符合我们的singleton方法(因为这样你就会在某个地方有基础对象和派生对象)。

引用是指所引用对象的所有用途。分配给引用就是分配给所引用对象。在您的情况下,对象没有数据成员,因此没有效果。

特别是没有可检测的切片。

申报

Base& r = Derived::derived;

…是一种非常不同的情况:初始化,而不是赋值。

初始化使引用引用引用指定的对象。

在此之后不能更改引用。


其他新闻:

  • 全局变量(引用)存在静态初始化顺序失败的风险。它们只是用来抵消单例getter函数的优势。有了全局变量,函数就没有意义了。

  • 具有数据成员的可变singleton允许在代码中广泛分离和看似不连接的部分之间进行通信。这使得我们很难依赖任何关于当前状态和影响因素的假设。这也是为什么全球变量被认为是邪恶的&贸易;在每种编程语言中,都要小心使用singleton。

相关内容

  • 没有找到相关文章

最新更新