C++ - Calling the non default constructor in a nested template instantiation - c++

I'm currently working to instantiate an Array of Stacks of cards for a FreeCell implementation. Each of the Array, Stack, and Card classes are created by me, and the Stack and Array classes are templated.
Array<StackRA<Card>> * hometemp = new Array<StackRA<Card>>(4);
When this code runs, it instantiates an Array of size 4, as intended, but calls the default constructor on the Stack, which I need to instantiate to size 13.
I've tried the following:
Array<StackRA<Card>> * hometemp = new Array<StackRA<Card>(13)>(4)
Array<StackRA<Card>> * hometemp = new Array<StackRA<Card>13>(4)
The Stack's default constructor is as follows:
template <typename T>
StackRA<T>::StackRA() : m_stack()
{
size = 0;
}
As well as the 1 arg constructor that I want to call
template <typename T>
StackRA<T>::StackRA( int data ) : m_stack( data )
{
size = data;
}
I've tried my luck on Google, but it keeps leading me to resources on templates inside template classes. Any thoughts are appreciated.
EDIT: Stepping into my program leads to the Array constructor
template<class T>
Array<T>::Array(int length, int start_index)
{
if (length < 0)
{
cout << "Length cannot be negative, length has been defaulted to 0\n";
m_length = 0;
}
else
{
m_length = length;
m_start_index = start_index;
m_array = new T[length];
}
}
The problem, more specifically, is that the "m_array = new T[length];" calls the StackRA default constructor. The question still stands, how can the non default constructor be called. Where in the instantiation would I place the size data I want to pass in?
EDIT2: I've solved this issue for this specific implementation using:
template <typename T>
StackRA<T>::StackRA() : m_stack()
{
size = 13;
}

First, if Array represents an array, and is meant to be used like one, then you don't need to create a pointer. You can construct it as an automatic object:
Array<StackRA<Card>> hometemp(4);
Now the problem as you say is with the following code calling the default-constructor for each element in the array:
m_array = new T[length];
This is true, the default-constructor will be called, initializing each constructor as T(). Prior to C++11, it was not possible to initialize each element using a different constructor, but now you are able to initialize using an initializer-list:
m_array = new T[length] { T(13), T(13), ... };
Of course this wouldn't be preferable for a variable-sized array (or an array with a very large size), and it's not for our purposes. Instead, you can follow the behavior of std::vector and create another constructor that copies an object to each element in the array:
template <typename T>
StackRA<T>::StackRA(int size, const T& obj) : size(size), m_stack(/* ... */)
{
m_array = new T[size];
std::fill(m_array, m_array + size, obj);
}
Now when constructing the array you can pass the correct constructor:
Array<StackRA<Card>> hometemp(4, StackRA<Card>(13));

Is there anything special in your Array so you cannot use std::vector? With std::vector, you would simply say
std::vector<StackRA<Card>> * hometemp =
new std::vector<StackRA<Card>>(4, StackRA<Card>(13));
Better, if the size is always fixed, use std::array, e.g.
std::vector<std::array<Card, 13>> * hometemp =
new std::vector<std::array<Card, 13>>(4);
or
std::array<std::array<Card, 13>, 4> * hometemp =
new std::array<std::array<Card, 13>, 4>();
or even
using Stack = std::array<Card, 13>;
using Deck = std::array<Stack, 4>;
Deck *hometemp = new Deck();
If you want to add functionality, you can simply encapsulate standard library containers into your own types.
Also, you might reconsider your use of the free store and especially raw pointers:
Deck hometemp;
If you insist on your own implementations, you might have a look at std::vector constructors for ideas.

Related

Initialization of values before constructor

Problem:
I implemented this new opeator for my class.
void* Objects::MemoryObject::operator new(size_t size, Memory::BaseAllocator* allocator) {
Objects::MemoryObject* newObject = static_cast<Objects::MemoryObject*>(allocator->allocateItem(size));
newObject->_objectAllocator = allocator;
newObject->_objectSize = size;
return newObject;
}
It will allocate memory for object and set attributes for object size and allocator used in allocation. Problem is that these values will be removed by constructor (object size will be 0, pointer to allocator will be NULL) even if i don't initialize them in code. How to avoid this? Is there any way to tell compiler that these attributes are initialized before constructor ?
What i tried: I tried to use volatile qualifier but it doesn't work
I think, you shouldn't use ordinal new for your task. Use something like "fabric": specific function, it allocates memory, creates instance and fills additional values.
Only thing that works is adding one structure that is holding informations. These informations are used later by constructor. This struct is defined in code file (.cpp) so it is invisible for other objects in program.
// Here we will save our values
struct {
Memory::BaseAllocator* allocator;
Memory::SystemInt size;
} MemoryObjectValues;
// we will take values from struct save them in attributes
Objects::MemoryObject::MemoryObject() {
this->_objectAllocator = MemoryObjectValues.allocator;
this->_objectSize = MemoryObjectValues.size;
MemoryObjectValues.allocator = nullptr;
MemoryObjectValues.size = 0;
}
// during allocation we will save values into struct
void* Objects::MemoryObject::operator new(size_t size, Memory::BaseAllocator* allocator) {
Objects::MemoryObject* newObject = static_cast<Objects::MemoryObject*>(allocator->allocateItem(size));
// set important values like size and pointer to allocator
MemoryObjectValues.allocator = allocator;
MemoryObjectValues.size = size;
return newObject;
}

How to initialize an array pointer object

I have a little problem to initialize (constructor) an array pointer of object. See the class below. Class test has 2 variable member, a pointer (value) that will be an array, and his size (size); and a constructor with parameters, and a destructor. In main function, I will create an array pointer of objects, and I have problem with it. If I create a single object like:
test obj(4); it will create a object, and his instance, value array is big 4.
Then if i want to create an array of objects:
test *obj;
obj = new test[2]{4,7};
I will create 2 object: obj[0] that is big 4, and obj[1] that is big 7.
So if I want to create more object:
test *obj;
obj=new test[100]{/*here I must write 100 numbers*/}
and this is the problem.
Because I cant write something like this:
test *obj;
obj=new int[100]{4}
I want that each value[] (instance of test class) is big 4, and I wont write 100 times "4".
I thought the analogy of declaring array:
If I write int array[5]={0,0,0,0,0}, I must write 4 times "0", or I can write also:
int array[5]={0} and each value is set to 0. (it's also true that if write int array[5]={5}, first index will be 5 and others 0).
Should I use a default constructor? What should I do?
#include <iostream>
using namespace std;
class test
{
private:
int* value;
int size;
public:
test(int size)
{
this->size = size;
value = new int[size];
}
~test()
{
delete[]value;
}
};
You can allocate the memory on the stack and get rid of dynamic allocation and memory management.
test array[100];
std::fill(std::begin(array), std::end(array), test(100));
Note that you would need a default constructor here.
You can iterate over your pointer to initialize each element
test *obj = new test[100];
for(size_t i = 0; i != 100; ++i)
{
obj[i] = test(/*parameters*/);
/* Remember to provide a move assignment operator
which invalidates the pointer member, otherwise when the
temporary variable is destroyed the new object pointer
member will point to data no more available*/
}
// ...
delete [] obj;
However it would be better to use std::vector
std::vector<test> obj(100, test(/*parameters*/));
Using std::vector your test object is initialized 100 times passing its arguments, using a pointer the allocation (new test[100]) will default construct every element, then you are going to assign each element the new value, that's why std::vector is a better solution to your problem

C++ value initialize items of a custom container

Lets take custom vector implementation as an example:
template<typename Object>
class myVector {
public:
explicit myVector(int size = 0) :
_size{ size },
_capasity{ size + SPARE_CAPACITY }
{
_buff = new Object[_capasity];
if (_size > 0) {
for (int i = 0; i < _size; i++) {
//_buff[i] = 0;
}
}
}
// more code
private:
Object * _buff = nullptr;
int _size;
int _capasity;
};
So my question is, how to make myVector be value-initialized in case I'll initialize it as:
int main() {
myVector<int> v02(5);
}
Here, it contains 5 int values, so I need it to be all zeros; same with other types. I commented out _buff[i] = 0; as it's specific to int. Please give me some hints.
It's as simple as
for (int i = 0; i < _size; i++)
_buff[i] = Object{};
Alternatively, you could get rid of the loop and add a pair of {} (or ()) here:
_buff = new Object[_capasity]{};
// ^^
But this option would value-initialize all _capasity objects, rather than the first _size ones, as noted by #bipll.
Also, note that if you want to mimic the behavior of std::vector, you need to allocate raw storate (probably std::aligned_storage) and call constructors (via placement-new) and destructors manually.
If Object is a class type, _buff = new Object[_capasity]; calls default constructors for all _capasity objects, rather than for the first _size objects as std::vector does.
Note that when calling
_buff = new Object[_capasity];
(btw, why have you moved this initialization out of init-list, into constructor body?) you already have default-initialized _capasity objects. Default initialization has the following effects here: while elements of scalar type would remain uninitialized (and reading from them UB), for class types you have already called _capasity constructors.
To avoid unnecessary constructions you have the following possible options, among others:
Use std::aligned_alloc to allocate non-initialized memory:
explicit myVector(std::size_t size = 0) :
size_{ size }
, capacity_{ size + SPARE_CAPACITY }
, buff_{std::aligned_alloc(alignof(Object), _capacity)}
{
if(!buff_) throw std::bad_alloc();
if(size) new (buff_) Object[size]{}; // empty braces answer your original query
}
Remember that again buff_ should be aligned_alloced when vector grows (can be std::realloc()ed for trivial types), and in destructor it should be std::free()d — and prior to that size_ objects inside it should be destructed (with an explicit call to ~Object()).
Change buff_'s type to something more trivial yet properly aligned:
using Storage = std::aligned_storage_t<sizeof(Object), alignof(Object)>;
Storage *buff_;
Object *data_ = nullptr;
public:
explicit myVector(std::size_t size = 0) :
size_{ size }
, capacity_{ size + SPARE_CAPACITY }
, buff_{new Storage(_capacity)}
{
if(size) data_ = new (buff_) Object[size]{};
}
Again, in destructor, objects should be manually destroyed, but this time buff_ can be simply delete[]d afterwards.

Constant pointer to array as class field C++

So i would like to have a constant pointer as field in my class becouse it must ALWAYS point to first cell. Problem is I can't do it since I am allocating memory in constructor. I was thinking about inicialization list but memory allocated is dependent on size of arguments; in C# I'd use ,,readonly''. No idea how to do it in C++
class Package{
private: char *const pack ; // <-- here
public: Package(PackInfo pckInfo, Data file) ;
public: ~Package();
};
Package::Package(PackInfo pckInfo, Data data){
this->headerSize = sizeof(pckInfo);
this->sizeOfData = data.GetDataSize();
//alocate memory
this->pack = new char[this->sizeOfData + this->headerSize](); //<- can not be done
//pointer on the begining of allocated array
PackInfo *temp = (PackInfo*) this->pack;
//putting header information in the begining of the array // moving pointer at cell just after header information
*temp = pckInfo; temp++;
char *packPointer = (char*)temp;
//getting data from file direclty into the array
data.GetCurrentBytes(packPointer);
}
I was thinking about inicialization list but memory allocated is dependent on size of arguments;
That doesn't prevent you:
Package::Package(PackInfo pckInfo, Data data):
headerSize(sizeof(pckInfo)),
sizeOfData(data.GetDataSize()),
pack(new char[this->sizeOfData + this->headerSize]())
{
// …
}
Just make sure both headerSize and sizeOfData are declared before pack in the class definition: The member initialization order is the same as their declaration order in the class body.
To initialise something constant in the constructor, use the member initialiser list e.g.
Package::Package(PackInfo pckInfo, Data data)
: pack = new char[required_size_here]
{
//... as you were
}
You need to make sure you have the sizes set before you use them. Left as an exercise for the reader.

creating an array of a given template (c++)

lately in my object oriented programming class we were dealing with templates.
in a question we got, we were asked to create a Queue class that can store any type
now my problem start when i want to store an array of somesort in this queue, for example:
Queue < char* >
now when i want to insert a new "node" to the queue i dont want to create a double pointing to a memory block. so basicly my question is: "how can i create an array of the same type of what the template class is pointing at?"
template<class T>
void Queue::enQueue(const T& value, int size = 1)
{
//exeptions handaling...
//handaling the case in wich the template is a pointer
if( _Is_pointer<T>() == true )
{
T temp = new T[size]; // i know its a mistake but thats what i basicly mean to do
for(int i = 0; i < size; i++)
temp[i] = value[i];
m_arr[++m_occupied] = temp; // m_arr is a data member of the T objects, m_occupied is as the name suggest
}
//...
}
thanks for the help :)
You could make template argument deduction work for you
// handling the case in wich the template is a pointer
template <class T> void Queue::enQueue(T const* value, int size = 1) {
This way, the overload deduces T as the type of object that value points at.
Now, you probably want to std::vector because you cannot treat arrays as simple values. Also, the use of new and delete for this kind of task is a code smell.
Guideline: In modern c++, vector<> is the default container for dynamically sized arrays, array<> for fixed-size arrays.
// handling the case in wich the template is a pointer
template <class T> void Queue::enQueue(T const* value, int size = 1) {
m_arr[++m_occupied] = temp(value, value + size);
}
BONUS You can even deduce arrays with size, if you're passing true references to arrays:
// handling the case in wich the template is an array reference
template <class T, size_t Size> void Queue::enQueue(T const (&value)[Size]) {
m_arr[++m_occupied] = std::vector<T>(value, value + Size);
}