使用(以及)realloc调用构造函数

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

我正在使用类模板为家庭作业编写自己类型的std :: vector。我想尽可能高效,所以我更喜欢使用realloc功能而不是新功能。这导致了一个问题,因为当我为新成员分配内存时,它不会调用新成员的构造函数,这会导致很多问题。

我的班级模板的相关部分:

template<class T>
class myVec {
    T *m_data;
    unsigned m_size;
public:
    //some methods
    //some methods
    myVec<T>& resize(unsigned size) {
        if (size == 0)
        {
            delete[] m_data;
            m_data = nullptr;
            m_size = 0;
            return *this;
        }
        m_data = (T*)realloc(m_data, size * sizeof(T));  //should call to constructor of T here!
        m_size = size;
        return *this;
    };
    void push(const T& t) {
        m_data = (T*)realloc(m_data, ++m_size * sizeof(T));
        memcpy(m_data, &t, sizeof(t));
        //m_data[m_size - 1] = t;
    };
    ~myVec() { delete[] m_data; };
};

使用realloc(来自C,如malloc和free)以及new和delete是不对的?如果是这样,是否有调用构造函数的c ++的realloc函数?如果没有,我怎么能在分配后手动调用构造函数?

c++ templates memory-management constructor
2个回答
3
投票

是的,以这种方式使用realloc是根本和不可救药的错误。

例如,考虑一个有两个成员的类,其中一个是指向另一个成员的指针。如果你realloc该类的一个实例,指针将不再指向另一个成员,打破它。

查看放置new和移动语义或查看std::vector的现有实现。


1
投票

如果您坚持使用C运行时函数手动分配数组,则必须使用placement-new在分配数组后手动调用C ++构造函数。因此,您必须手动调用C ++析构函数。

此外,你的push()使用memcpy()作为非POD类型也是不安全的。您需要为推送的元素实现正确的复制/移动语义。

而且,不要忘记Rule of 5所以你的类也为自己实现了正确的复制/移动语义。

尝试更像这样的东西:

template<class T>
class myVec
{
    T *m_data = nullptr;
    unsigned m_size = 0;

public:
    ...

    myVec() = default;

    myVec(const myVec<T> &src)
    {
        if (src.m_size)
        {
            m_data = (T*) malloc(src.m_size * sizeof(T));
            if (!m_data) throw std::runtime_error("malloc failed!");

            for(unsigned i = 0; i < m_size; ++i)
                new (&m_data[i]) T(src.m_data[i]);

            m_size = src.m_size;
        }
    }

    myVec(myVec<T> &&src)
    {
        std::swap(m_data, src.m_data);
        std::swap(m_size, src.m_size);
    }

    ~myVec()
    {
        resize(0);
    }

    myVec<T>& resize(unsigned size)
    {
        if (m_size != size)
        {
            T *temp;

            if (size)
            {
                temp = (T*) malloc(size * sizeof(T));
                if (!temp) throw std::runtime_error("malloc failed!");
            }
            else
                temp = nullptr;

            unsigned numToMove = std::min(m_size, size);

            for(unsigned i = 0; i < numToMove; ++i)
                new (&temp[i]) T(std::move(m_data[i]));

            if (size < m_size)
            {
                for(unsigned i = m_size; i-- > size; )
                    m_data[i-1].~T();
            }
            else
            {
                for(unsigned i = m_size; i < size; ++i)
                    new(m_data[i]) T();
            }

            m_data = temp;
            m_size = size;
        }

        return *this;
    };

    void push(T t)
    {
        resize(m_size + 1);
        m_data[m_size - 1] = std::move(t);
        return *this;
    }

    myVec<T>& operator=(myVec<T> rhs)
    {
        std::swap(m_data, rhs.m_data);
        std::swap(m_size, rhs.m_size);
        return *this;
    }

    ...
};

也就是说,你应该考虑在你的类中添加一个m_capacity成员,以便在需要重新分配数组时减少:

template<class T>
class myVec
{
    T *m_data = nullptr;
    unsigned m_size = 0;
    unsigned m_capacity = 0;

public:
    ...

    myVec() = default;

    myVec(const myVec<T> &src)
    {
        if (src.m_size)
        {
            reserve(src.m_size);

            for(unsigned i = 0; i < src.m_size; ++i)
                new (&m_data[i]) T(src.m_data[i]);

            m_size = src.m_size;
        }
    }

    myVec(myVec<T> &&src)
    {
        std::swap(m_data, src.m_data);
        std::swap(m_size, src.m_size);
        std::swap(m_capacity, src.m_capacity);
    }

    ~myVec()
    {
        resize(0);
        free(m_data);
    }

    myVec<T>& reserve(unsigned capacity)
    {
        if (capacity > m_capacity)
        {
            T *temp = (T*) malloc(capacity * sizeof(T));
            if (!temp) throw std::runtime_error("malloc failed!");

            for(unsigned i = 0; i < m_size; ++i)
                new (&temp[i]) T(std::move(m_data[i]));

            m_data = temp;
            m_capacity = capacity;
        }

        return *this;
    };

    myVec<T>& resize(unsigned size)
    {
        if (m_size != size)
        {
            if (size < m_size)
            {
                for(unsigned i = m_size; i-- > size; )
                    m_data[i-1].~T();
            }
            else
            {
                reserve(size);
                for(unsigned i = m_size; i < size; ++i)
                    new(m_data[i]) T();
            }

            m_size = size;
        }

        return *this;
    };

    void push(T t)
    {
        if (m_size == m_capacity) reserve(m_size * 1.5);
        new(m_data[m_size]) T(std::move(t));
        ++m_size;
        return *this;
    }

    myVec<T>& operator=(myVec<T> rhs)
    {
        std::swap(m_data, rhs.m_data);
        std::swap(m_size, rhs.m_size);
        std::swap(m_capacity, rhs.m_capacity);
        return *this;
    }

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