【问题标题】:C++11 GCC 4 Fast Optimization don't store implementation class from abstract class in the stackC ++ 11 GCC 4快速优化不在堆栈中存储抽象类的实现类
【发布时间】:2020-11-20 00:04:18
【问题描述】:

所以,伙计们,我有一个抽象类,另一个类将这个类的实现存储在堆栈中(我不想要堆分配,而且我不知道在不让调用者明确声明的情况下其他方法实现)和另一个存储此接口类的引用。但是,GCC似乎没有将实现类存储在堆栈中,并且在使用接口类时可能找不到实现类vtable。

基本上,在没有优化的情况下使用 GCC 4.8.1 编译时一切正常,但是当我尝试使用它时,程序崩溃然后返回 139。

我不知道为什么 GCC 4 不支持它,而 GCC 5 支持,但我看到它们会生成不同的指令。

编译器资源管理器:https://godbolt.org/z/Wfvj65

#include <cstdio>

#define FORCEINLINE inline __attribute__((always_inline))

class IFormatter
{
public:
    virtual void Format(const void* InData) const = 0;
};

template<typename T>
class TFormatter :
    public IFormatter
{
public:
    TFormatter() = delete;
};

using Scalar = float;

// Implemented, fine.
struct RVector2
{
    Scalar X;
    Scalar Y;
};

// Not implemented, get error.
struct RVector3
{
    Scalar X;
    Scalar Y;
    Scalar Z;
};

template<>
class TFormatter<RVector2> :
    public IFormatter
{
public:
    virtual void Format(const void*) const override
    {
        printf("[RVector2]\n");
    }
};

template<typename T>
class TCustom
{
public:
    FORCEINLINE TCustom(const T& InValue) :
        Value(InValue),
        Format(TFormatter<T>{})
    {
    }

    FORCEINLINE const T* Data() const
    {
        return &Value;
    }

    FORCEINLINE const IFormatter& Formatter() const
    {
        return Format;
    }

private:
    const T& Value;
    TFormatter<T> Format;
};

template<typename T>
FORCEINLINE TCustom<T> MakeCustom(const T& InValue)
{
    return TCustom<T>{ InValue };
}

class RCustom
{
public:
    FORCEINLINE RCustom(const void* InValue, const IFormatter& InFormatter) :
        Data(InValue),
        Formatter(InFormatter)
    {
    }

    template<typename T>
    FORCEINLINE RCustom(TCustom<T> const& InCustom) :
        RCustom(InCustom.Data(), InCustom.Formatter())
    {
    }

    FORCEINLINE const IFormatter& Get() const
    {
        return Formatter;
    }

private:
    const void* Data;
    const IFormatter& Formatter;
};

int main()
{
    const RVector2 Vector{};
    const RCustom Custom = MakeCustom(Vector);
    Custom.Get().Format(nullptr);
    
    return 0;
}

【问题讨论】:

  • 一个对象对其成员的存储位置没有影响,这在对象的创建者手中。
  • 我可能遗漏了一些东西 - 但这应该如何工作?您创建一个TCustom&lt;RVector2&gt; 对象并将结果存储在一个看似无关的类型RCustom?注意RCustomTCustom 之间的差异——一个通过引用存储IFormatter,另一个通过值存储TFormatter。如果从RCustom 中删除&amp;,则代码有效。

标签: c++ c++11 gcc visual-c++ abstract-class


【解决方案1】:

正如其中一位 cmets 所说,将 TCustom 存储在不相关的类型 RCustom 中会发生一些奇怪的事情。隐式构造函数RCustom(TCustom) 让我失望了。

这个问题非常微妙。如果某些东西适用于 -O0 但不适用于 -Ofast(或 -O2/-O3),那么大多数时候内存会发生一些有趣的事情。正如Benny K 所说,在您的情况下,问题是RCustom 仅存储对IFormatter 的引用:

class RCustom {
    ...
    const IFormatter& Formatter; // Asking for problems
}

这似乎是一个无辜的&amp;,但实际上这是危险的。因为此成员的有效性取决于外部对象的生命周期。有几种方法可以解决这个问题。您可以在RCustom 中保存TFormatter 的副本(而不是参考):

template<typename T>
class RCustom {
    ...
    const TFormatter<T> Formatter;
}

但这也意味着你必须放弃抽象接口IFormatter 来换取具体接口TFormatter&lt;T&gt;。要在 C++ 中使用虚方法,您需要一个指针,但使用原始指针将引入与引用相同的内存问题。所以我建议你使用智能指针:

class RCustom {
    ...
    std::shared_ptr<const IFormatter> Formatter;
}

PS:确切地说出了什么问题:在MakeCustom() 中,您初始化了一个TCustom 对象,该对象初始化并复制了TFormatter 的一个实例。接下来对TCustomTFormatter 实例的引用保存在RCustom 中。现在这个RCustom 对象被返回并且函数MakeCustom() 被清理了。在此清理过程中,TCustom 被销毁,TFormatter 成员也是如此。但是RCustom 仍然保留对这个无效内存的引用。在 C++ 中,&amp; 和没有 &amp; 之间的区别非常重要。

【讨论】:

  • 我对此并不完全确定,但这是我的猜测。无论如何,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-21
  • 1970-01-01
  • 2015-03-13
  • 2016-02-20
相关资源
最近更新 更多