i'm writing my own type of std::vector for homework assignments,using class templates. i wan't it to be efficient as possible so i prefer to use realloc function rather then new. this causes me a problem because when i allocate memory to new member it doesn't call to the constructor of the new member and these causes a lot of problems.
the relevant part of my class template:
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; };
};
Does it wrong to use realloc(from C like malloc and free) along with new and delete? if so, is there realloc like function for c++ that calls to constructor? if not,how can i call manually call to the constructor after the allocation?
You can't mix malloc()
/realloc()
/free()
with new
/delete
. Use one or the other.
If you insist on manually allocating the array using the C runtime functions, you will have to use placement-new to manually call C++ constructors AFTER you have allocated the array. And, as such, you will have to manually call C++ destructors.
Also, it is not safe for your push()
to use memcpy()
for non-POD types, either. You need to implement proper copy/move semantics for pushed elements.
And, don't forget the Rule of 5 so your class implements proper copy/move semantics for itself, too.
Try something more like this instead:
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);
}
void 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].~T();
}
else
{
for(unsigned i = m_size; i < size; ++i)
new(&temp[i]) T();
}
free(m_data);
m_data = temp;
m_size = size;
}
};
void push(T t)
{
resize(m_size + 1);
m_data[m_size - 1] = std::move(t);
}
myVec<T>& operator=(myVec<T> rhs)
{
std::swap(m_data, rhs.m_data);
std::swap(m_size, rhs.m_size);
return *this;
}
...
};
That said, you should consider adding an m_capacity
member to your class so you can reduce when you need to reallocate the array:
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);
}
void 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]));
free(m_data);
m_data = temp;
m_capacity = capacity;
}
};
void resize(unsigned size)
{
if (m_size != size)
{
if (size < m_size)
{
for(unsigned i = m_size; i-- > size; )
m_data[i].~T();
}
else
{
reserve(size);
for(unsigned i = m_size; i < size; ++i)
new(&m_data[i]) T();
}
m_size = size;
}
};
void push(T t)
{
if (m_size == m_capacity) reserve(m_size > 0 ? m_size * 1.5 : 4);
new(&m_data[m_size]) T(std::move(t));
++m_size;
}
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;
}
...
};