繁体   English   中英

为什么我不能同时实例化对基类的引用和对派生类的指针?

[英]Why can't I instantiate a reference to a base class at the same time as a pointer to a derived class?

我的问题的最简单示例可以在以下代码段中看到:

class Handle : public IHandle_<Handle>{
    public:
        Handle(std::unique_ptr<Derived> aDerived)
            : derived(std::move(aDerived)),
              base(*aDerived) {};

        std::unique_ptr<Derived> derived;
        Base& base;
};

在这里,您可以看到Handle类实际上是Derived的包装器。 更重要的是,我希望通过引用公开DerivedBase的基类。 原因是,最终我希望Handle看起来像这样:

class Handle : public IHandle_<Handle>{
    private:
        std::unique_ptr<Derived1> myD1;
        std::unique_ptr<Derived2> myD2;

    public:
        Handle(std::unique_ptr<Derived1> aD1)
            : myD1(std::move(aD1)),
              base1(*aD1),
              base2(*aD1){};
        Handle(std::unique_ptr<Derived2> aD2)
            : myD2(std::move(aD2)),
              base1(*aD2),
              base2(*aD2){};

        Base1& base1;
        Base2& base2;

};

我希望Handle能够像这样工作的原因是,我将其用作“实体组件系统”中的“组件”,并且我希望该特定组件可以从相同的两个不同的具体实现中实例化。基类。 我之所以这么说是因为从定义上讲,“实体组件系统”设计模式不同于传统的面向对象的编程实践:换句话说,我知道还有其他方法可以完成我想做的事情,但是我希望使其能够在我在这里列出的内容有些变化。

为什么第一个代码片段中显示的简单Handle示例失败? 尝试访问Base的方法时,它可以编译良好但存在段错误。 如果更改实例化Handle成员变量的顺序,则在编译时会出现一些错误,我认为这可能会提供一些提示,但我并不真正了解发生了什么。

这是Handle及其依赖的类的完整工作示例:

#include <memory>
#include <iostream>

class Base{
    public:
        Base(int ax) : x(ax){};
        virtual ~Base() = 0;
        virtual void setVal(float a) = 0;
        virtual float getVal() = 0 ;

        int x;
};

Base::~Base(){}

class Derived : public Base{
    public:
        Derived(int ax, int az)
            : Base(ax), z(az){};

        int z;
};

class Concrete : public Derived{
    public:
        Concrete(int ax, int aw, int av)
            : Derived(ax, aw),
              v(av){};
        void setVal(float a) override{
            myVal = a;
        }
        float getVal() override{
            return myVal;
        }
        float myVal;
        int v;
};

class IHandle{
    public:
        virtual ~IHandle() = 0;
};

IHandle::~IHandle(){}

template <class T>
class IHandle_ : public IHandle{
    public:
        virtual ~IHandle_() = 0;
};

template <class T>
IHandle_<T>::~IHandle_(){};

class Handle : public IHandle_<Handle>{
    public:
        Handle(std::unique_ptr<Derived> aDerived)
            : derived(std::move(aDerived)),
              base(*aDerived) {};

        std::unique_ptr<Derived> derived;
        Base& base;
};


int main(){
    // These two pointers are owned by an EntityManager
    std::unique_ptr<Derived> ptr(new Concrete(1, 2, 3));

    // we can get a reference to an IHandle from the EntityManager
    std::unique_ptr<IHandle> aHandle(new Handle(std::move(ptr)));

    // We need, specifically, a `Handle` implementation of `IHandle`
    Handle& handle = static_cast<Handle&>(*aHandle);
    // seg fault on the following line
    handle.base.setVal(10.0);
    std::cout << "a = " << handle.base.getVal() << std::endl;
    return 0;
}

C ++类中的成员按照您声明它们的顺序进行初始化,因此从第一个代码片段开始,Handle类中成员的初始化顺序为:

  • 派生的
  • 基础

就是说,这意味着在构造函数中

derived(std::move(aDerived))

aDerived的内部资源aDerivedderived ,合理地重置aDerived的状态。 因此,只要您的代码到达语句

base(*aDerived)

base将引用一个空对象 (取决于您在Base和Derived类中的move构造函数实现),该对象很可能在构造函数本身调用后从内存中删除。

因此,我相信您在代码中获得的对base任何引用都指向未分配的内存,从而导致SEG_FAULT错误。

SEG_FAULT大部分时间是指正在使用的代码(在编写本示例时,请参见setval())未为运行中的进程分配(尚未分配)的内存区域。

希望这会有所帮助,晚安,斯特凡诺

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM