Reputation: 3128
I have a class Array
which has the following constructor:
explicit Array(int size = 0, const T &v = T()) {
if (size < 0) {
throw ArrayExceptions::InvalidLength();
}
Array::size = size;
array = new T[size];
insertValueIntoArray(size,v);
}
In some other class DataController
I have:
Array<Data> dataArray;
int dataAmount;
explicit DataController(int length) : dataArray(length), dataAmount(length) {}
But Data.h
does not have a constructor without arguments so the compiler complaints on const T &v = T()
of Array.h
:
error: no matching function for call to 'Data::Data()'
Instead it has the following constructor:
Data(int length) : length(length), /** Other constructor calls ... **/ {}
What should I change in order to make the Array
use the Data(length)
constructor insead of the Data()
?
I can modify all the files.
I have tried to switch it to:
explicit DataController(int length) : dataArray(length, Data(length)), dataAmount(length) {}
but then I get the same error in line:
array = new T[size];
Minimal example:
template<typename T>
class Array {
int size;
T *array;
public:
explicit Array(int size = 0, const T &value = T()) {
Array::size = size;
array = new T[size];
}
~Array() {
delete[] array;
}
};
class Data {
private:
int length;
public:
Data(int length) : length(length) {}
};
class DataController {
private:
Array<Data> dataArray;
int dataAmount;
public:
explicit DataController(int length) : dataArray(length), dataAmount(length) {}
};
Please suggest solutions without using the std
if you can.
Upvotes: 1
Views: 114
Reputation: 62636
Rather than calling new T[size]
, you can allocate uninitalised memory then copy-construct T
s into it.
template<typename T>
class Array {
struct aligned_storage {
struct type {
alignas(alignof(T)) unsigned char data[sizeof(T)];
};
};
using storage_t = aligned_storage::type;
int size;
storage_t * storage;
void clear() {
for (int i = 0; i < size; ++i)
reinterpret_cast<T*>(storage.data[i])->~T();
delete[] data;
}
public:
explicit Array(int size = 0, const T & value = T())
: size(size), storage(new storage_t[size])
{
for (int i = 0; i < size; ++i)
new (storage.data[i]) T(value);
}
Array(const Array & other)
: size(other.size), storage(new storage_t[other.size])
{
for (int i = 0; i < size; ++i)
new (data[i]) T(other[i]);
}
Array(Array && other)
: size(other.size), storage(other.storage)
{
other.size = 0;
other.data = nullptr;
}
Array& operator=(Array other)
{
clear();
storage = other.storage;
size = other.size;
}
~Array() {
clear();
}
T& operator[](int pos) {
return *reinterpret_cast<T*>(storage.data[pos]);
}
const T& operator[](int pos) const {
return *reinterpret_cast<T*>(storage.data[pos]);
}
};
Upvotes: 0
Reputation: 36379
You have 2 issues in your code.
The first is the default value of v
in Array
is T()
which requires a default constructor. This is easily solved by just not using the default parameter:
dataArray(length, Data(length))
The second more complicated issue is then that:
array = new T[size]
calls the default constructor for each element in array
. The simplest solution is to use an existing class that handles this for you like std::vector
. If you want to implement it yourself you'll need to allocate memory using malloc
, ::operator new
or maybe std::aligned_storage
then use placement new to initialise each element to a copy of v
. You'll need to keep track of both how many elements you've allocated and how many you have initialised then call the destructors only for the initialised elements.
Upvotes: 1