我正在使用类模板为家庭作业编写自己类型的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函数?如果没有,我怎么能在分配后手动调用构造函数?
是的,以这种方式使用realloc
是根本和不可救药的错误。
例如,考虑一个有两个成员的类,其中一个是指向另一个成员的指针。如果你realloc
该类的一个实例,指针将不再指向另一个成员,打破它。
查看放置new和移动语义或查看std::vector
的现有实现。
如果您坚持使用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;
}
...
};