伙计们,我有一个抽象类,另一个是将此类的实现存储在堆栈中的其他类(我不希望堆分配,而且我不知道在不使调用者显式声明实现的情况下实现此目的的其他方法)另一个存储此接口(interface)类的引用。但是,似乎GCC不会将实现类存储在堆栈中,并且当使用接口(interface)类时,可能找不到实现类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
存储在不相关的RCustom
类型中有些奇怪的事情。隐式构造函数RCustom(TCustom)
让我失望了。
这个问题很微妙。如果某些东西适用于-O0而不适用于-Ofast(或-O2 / -O3),则大多数情况下,内存发生了一些有趣的事情。正如Benny K所说,在您的情况下,问题是RCustom
仅存储对IFormatter
的引用:
class RCustom {
...
const IFormatter& Formatter; // Asking for problems
}
这似乎是无辜的&
,但实际上这很危险。因为此成员的有效性取决于外部对象的生存期。有几种解决方法。您可以将TFormatter
的副本保存在RCustom
中(而不是引用):template<typename T>
class RCustom {
...
const TFormatter<T> Formatter;
}
但这也意味着您必须放弃抽象接口(interface)IFormatter
来使用具体的TFormatter<T>
。要在C++中使用虚拟方法,您需要一个指针,但是使用原始指针会带来与引用相同的内存问题。因此,我建议您使用智能指针:class RCustom {
...
std::shared_ptr<const IFormatter> Formatter;
}
PS:确切地说出问题所在:在MakeCustom()
中,您初始化了TCustom
对象,该对象初始化并复制了TFormatter
的实例。接下来,将对TFormatter
中的TCustom
实例的引用保存在RCustom
中。现在,返回此RCustom
对象,并清理了MakeCustom()
函数。在此清洁过程中,TCustom
被破坏,TFormatter
-member也被破坏。但是RCustom
仍然保留对此无效内存的引用。在C++中,&
和没有&
之间的区别非常重要。