新建和放置新立即调用类析构函数

问题描述 投票:0回答:1

这是我遇到过的一件有点奇怪的事情。这是一个项目的一部分,我正在制作一个竞技场分配器和智能指针,目标是将所有东西都包裹在过量的泡沫包装中,看看它最终会在哪里,只是为了个人体验。创建对象后立即调用析构函数 - 甚至在函数的 return 语句之前!不知何故,作用域要么小于创建它的函数,要么正在调用析构函数。我的

bound_ptr
静态创建器和分配器中返回
bound_ptr
的函数都遇到此问题。

我可能在提供问题信息和代码时遗漏了一些东西,现在是凌晨 1:30,我很累。我会在大约 8 小时后回来查看。

bound_ptr.hpp

#include <iostream>
template <typename T>
class bound_ptr
{
private:
    T* ptr = nullptr;
    size_t size = 0;
    void (*deallocator)(void* mem, void* userdata) = nullptr;
    void* userdata = nullptr;
    bool managed = false;
public:
    bound_ptr(const bound_ptr&) = delete;
    bound_ptr& operator =(const bound_ptr&) = delete;
    bound_ptr(bound_ptr&& other) noexcept
    {
        this->ptr = other.ptr;
        this->size = other.size;
        this->deallocator = other.deallocator;
        this->userdata = other.userdata;
        this->managed = other.managed;
        other.ptr = nullptr;
        other.size = 0;
        other.deallocator = nullptr;
    }

    // The idea is to move the pointer into the class, so the class owns it explicitly
    // Just a note. I tested removing the '&&' and the issue still persists
    // element_count is used for arrays to check array bounds and prevent OOB accesses
    // dealloc function ptr is potentially called in the bound_ptr destructor
    // An extra pointer to be passed to the dealloc function when calling it
    // If false, the provided dealloc function will be called when this is destroyed
    bound_ptr(T*&& ptr, size_t element_count, 
              void (*dealloc)(void*, void*)=nullptr, 
              void* userdata=nullptr, bool managed=false) :
        ptr(ptr), size(element_count), deallocator(dealloc), userdata(userdata), managed(managed)
    {}
    // A wrapper around delete to make it work for the custom destructor
    static void _delete(void* mem, void* userdata) { delete (T*)mem; }
    
    ~bound_ptr()
    {
        if (this->ptr == nullptr)
            return;
        if (!this->managed)
            this->ptr->~T();
        if (this->deallocator != nullptr && this->ptr != nullptr)
            this->deallocator(this->ptr, this->userdata);
    }
    
    // This function is the culprit.
    // Should call the constructor of the type with the provided arguments
    template <typename... C>
    static bound_ptr<T> make_bound(C&&... args)
    {
        auto ptr = bound_ptr(
            new T(std::forward<T>(args)...), 1, _delete, nullptr, true
        );
        // Destructor already called for T?! How and why?
        std::cout << "PreReturn" << std::endl;
        return ptr;
    }
};

main.cpp

#include "bound_ptr.hpp"
struct A
{
    int id;
    A(int id) : 
      id(id)
    { std::cout << "Created " << id << std::endl; }
    ~A()
    { std::cout << "Destroyed " << this->id << std::endl; }
};

int main()
{
    bound_ptr<A> a = bound_ptr<A>::make_bound(69);
}

预期输出为;

Created 69
PreReturn
Destroyed 69

但是实际输出是;

Created 69
Destroyed 69
PreReturn

custom_allocator.hpp
- 未经过编译测试

template<typename T, typename... C>
inline bound_ptr<T> Alloc::allocate_bound(size_t num_elements, C&&... ctor_args)
{
    bound_ptr<T> a(
        std::move(
            (T*)this->allocate_bytes(num_elements * sizeof(T))
        ),
        num_elements,
        _bound_ptr_free,  // A static function to call the custom allocator's free
        this,
        true
    );
    // No output at all
    new (a.get()) T(std::forward<T>(ctor_args)...);
    // Outputs "Created" and "Destroyed" before returning
    return std::move(a);

}

当我使用 new 时,为什么以及如何调用析构函数?它应该位于堆上并且不是静态管理的,因此它永远不应该调用析构函数,除非我在放置新的情况下直接调用删除或析构函数。即使在调用析构函数之后,在类中的堆栈上分配的对象仍然有效,但这将导致任何使用堆的类出现问题。

c++ memory dynamic-memory-allocation destructor
1个回答
0
投票

一旦我删除了基本类型的复制和移动构造函数

T
,答案就出人意料地简单找到了——它是
std::forward
。我错误地认为
std::forward
会将模板参数包解压到构造函数中,但意外地导致调用移动或复制构造函数。

只需更换

new T(std::forward<T>(ctor_args)...)

new T(ctor_args...)

当您从标准库中复制随机位而不了解它为何使用、它做什么或如何做时,就会发生这种情况。

谢谢 yksisarvinen一些程序员兄弟以及其他人指出我的代码中的错误和问题。

© www.soinside.com 2019 - 2024. All rights reserved.