首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当基类被标记为虚拟时,正常块之后的损坏

当基类被标记为虚拟时,正常块之后的损坏
EN

Stack Overflow用户
提问于 2021-06-02 18:13:02
回答 1查看 60关注 0票数 2

在正常的块断言之后,我看到了一个奇怪的损坏,当我将基类设为非虚的时候,这个损坏就消失了。

我已经将范围缩小到实际的delete调用发生的时刻(原始源有智能指针,但我已经跟踪到这一点也发生在简单的new/delete中)。

奇怪的情况是,只有当基类被标记为virtual时才会发生这种情况;删除基类的virtual声明可以避免此错误。我知道虚拟基类会影响构造函数和析构函数的执行顺序,但由于我的类只有一个基类(这是一个充当接口的抽象类),我认为在这种情况下初始化顺序实际上不会改变。

“接口”:

代码语言:javascript
复制
class INetworkSender
{
public:
    /// <summary>
    /// Finalizes an instance of the <see cref="INetworkSender"/> class.
    /// </summary>
    virtual ~INetworkSender() {}

    /// <summary>
    /// Sends the specified data.
    /// </summary>
    /// <param name="data">The pointer to the buffer holding the data to send.</param>
    /// <param name="length">The amount of data to send.</param>
    virtual void send(const char* data, size_t length) = 0;

    /// <summary>
    /// Returns the remote address.
    /// </summary>
    virtual const std::string& address() const = 0;

    /// <summary>
    /// Returns the remote port.
    /// </summary>
    virtual uint16_t port() const = 0;
};

具体的实现类:

代码语言:javascript
复制
class UdpSender
    : virtual public INetworkSender
{
public:
    /// <summary>
    /// Initializes a new instance of the <see cref="UdpSender" /> class.
    /// </summary>
    /// <param name="address">The address to which to send.</param>
    /// <param name="port">The port to which to send.</param>
    UdpSender(const std::string& address, uint16_t port);

    /// <summary>
    /// Finalizes an instance of the <see cref="UdpSender" /> class.
    /// </summary>
    virtual ~UdpSender();

    /// <summary>
    /// Sends the specified data.
    /// </summary>
    /// <param name="data">The pointer to the buffer holding the data to send.</param>
    /// <param name="length">The amount of data to send.</param>
    virtual void send(const char* data, size_t length) override;

    /// <summary>
    /// Returns the remote address.
    /// </summary>
    virtual const std::string& address() const override { return this->m_address; }

    /// <summary>
    /// Returns the remote port.
    /// </summary>
    virtual uint16_t port() const override { return this->m_port; }

private:
    /// <summary>
    /// The socket handle. Note we use a void* to abstract away different OS specific implementations!
    /// </summary>
    void* m_handle;

    /// <summary>
    /// The target address.
    /// </summary>
    std::string m_address;

    /// <summary>
    /// The target port.
    /// </summary>
    uint16_t m_port;
};

最后,触发断言的代码:

代码语言:javascript
复制
    UdpSender* sender = new UdpSender("0.0.0.0", 0);
    delete sender;

为了确保这不是我在构造函数、析构函数或任何被调用的方法中所做的任何事情,我已经注释掉了类实现中的所有内容:

代码语言:javascript
复制
UdpSender::UdpSender(const std::string& address, uint16_t port)
    : m_address(address)
    , m_port(port)
{
    /*
    */
}

UdpSender::~UdpSender()
{
    /*
    */
}


void UdpSender::send(const char* data, size_t length)
{
    /*
    */
}

我是不是偶然发现了一个晦涩难懂的编译器错误?VS2019 (16.10.0),工具集v142,针对VS2019 C++17标准进行编译。

编辑:使用地址消毒器提供了更多信息:

线程中0x12135a727100上的==8416==ERROR: AddressSanitizer: new- delete -type-mismatch T1:传递给delete的对象类型错误:已分配类型的大小: 72字节;已释放类型的大小: 80字节。

Edit2:相同,但对于非虚拟基类仍然会触发AddressSanitizer,所以我认为它与类是否为虚拟类没有直接关系,尽管它确实会影响内存中对象的大小:

线程中0x12746dba3020上的==18300==ERROR: AddressSanitizer: new- delete -type-mismatch T1:传递给delete的对象具有错误的类型:已分配类型的大小: 60字节;已释放类型的大小: 64字节。

EN

回答 1

Stack Overflow用户

发布于 2021-06-02 21:40:31

地址消毒器报告的大小差异最终使我找到了问题的根本原因。

在代码库中的某个地方,我通过以下代码使用了一个带有自定义打包边界(4;默认值为8)的结构:

代码语言:javascript
复制
#pragma pack(4)
struct Foo { ... }

包含此结构的头文件的任何代码都将调整此包含之后的任何结构/类的大小(4字节边界),而不包括头文件或在结构/类定义之后包含头文件的代码(8字节边界)将导致同一结构/类具有两种不同大小的结构/类。

显然,打包应该只针对特定的结构,这是使用(MSVC特定的,我想是)实现的:

代码语言:javascript
复制
#pragma pack(push)
#pragma pack(4)
struct Foo { ... }
#pragma pack(pop)
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67803107

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档