Initialization of values before constructor - c++

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;
}

Related

Syntax to heap allocate anything?

Is there a syntax, template or function that allows me to essentially turn any value into a pointer to that value? I.e. copy it to the gc heap and return a pointer to it? "new" doesn't work for all types, std.experimental.allocator doesn't work in ctfe, and both seem to have troubles making pointers to delegates.
You can put the data in question inside a struct, then use the new keyword on that struct.
T* copy_to_heap(T)(T value) {
// create the struct with a value inside
struct S {
T value;
}
// new it and copy the value over to the new heap memory
S* s = new S;
s.value = value;
// return the pointer to the value
return &(s.value);
}
void main() {
// example use with a delegate:
auto dg = copy_to_heap(() { import std.stdio; writeln("test"); });
(*dg)();
}
That assumes you already have a value to copy but that's probably easier and the way you'd do it anyway. But you can also tweak the code to remove that requirement if you want (perhaps just pass typeof.init for example).

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.

Is it bad practice to reinitialize a pointer?

I have an Image class and initially I do not know the image dimensions, so I just initialize a data_ pointer to be an array of size 0. Later when I find the image information I reinitialize data_ to a new size. Will this create any problem in memory? and is there a cleaner way to do this?
Below is the class I have written:
class Image
{
private:
int numRows_, numCols_;
unsigned char* data_;
public:
Image() : numRows_(0), numCols_(0), data_(new unsigned char[0])
{}
void setData(int r, int c, unsigned char* data)
{
this->numRows_ = r;
this->numCols_ = c;
this->data_ = new unsigned char[r*c];
for (int i = 0; i < r*c; i++)
{
this->data_[i] = data[i];
}
}
int rows();
int cols();
unsigned char* data();
~Image();
};
Thanks in advance
This will in fact leak memory. The call to new allocates memory for the array, even if it is empty. As soon as you reassign data_, the previous array is leaked and can no longer be freed.
You can either make sure you delete[] any new[] you allocate, or just don't allocate an empty array and instead set data_ to nullptr until you have meaningful data to use.
An even better idea is don't allow the creation of an object in an invalid state, require the data in the constructor - see RAII:
In RAII, holding a resource is a class invariant, and is tied to
object lifetime: resource allocation (or acquisition) is done during
object creation (specifically initialization), by the constructor,
while resource deallocation (release) is done during object
destruction (specifically finalization), by the destructor.
If you do decide to keep setData, then as mentioned in comments, you also must make sure to delete[] existing data in setData before reassigning data_, in case the method is called more than once.
I think a cleaner way to do so will be using a vector:
std::vector<unsigned char> v; // vector with size 0
v.resize(r*c); // after size is known, just resize

Using a Constructor through pointer

I have a problem.
The compiler keeps warning me for invalid use of the constructor.
All i wanted to do is to create a new course in the class. whats wrong?
int StArray::addCS_Course(int id, int CourseNum, char* CourseName,int HwNum, float HwWeigh, bool Takef, char* BookName){
int i;
CS_Course* course;
if ((CourseNum<0)||(HwNum<0)||(HwWeigh<0)||(HwWeigh>1))
return 0;
for (i=0;i<StudentNum_;i++){
if (Arr_[i]->getID()==id) {
course=(CS_Course*)malloc(sizeof(CS_Course*));
if (course==NULL) {
fprintf(stderr,"Malloc failed\n");
exit(0);
}
course->CS_Course::CS_Course(CourseNum,CourseName,HwNum,HwWeigh,Takef, BookName);
if (Arr_[i]->addCS_Course(course)==1)
return 1;
else
{
free(course);
return 0;
}
}
}
return 0;
}
To create a new object in C++, you don't do this:
course = (CS_Course*) malloc(...);
course->CS_Course::CS_Course(...);
you do this:
course = new CS_Course(...);
That code looks after both allocating memory and calling the constructor.
You then delete your object with delete course; rather than free(course);
(But as juanchopanza points out in the comments, it's considered bad form to create objects on the heap in C style like this - you should prefer to use standard library containers and avoid the use of new. That's a whole nother discussion - you might want to read a tutorial on modern C++.)
Edit by #RemyLebeau: If you need to construct an object in existing memory, use placement new instead:
buffer = malloc(...);
course = new (buffer) CS_Course(...);
But then you have to call the destructor manually:
course->~CS_Course();
free(buffer);
malloc(sizeof(CS_Course*)) allocates enough space for a pointer to a CS_Course, not a CS_Course itself. If malloc were the right way to dynamically allocate memory for an object, you would need to call it like this:
malloc(sizeof(CS_Course));
However, malloc isn't the right way to do this; in C++, you use new to dynamically allocate memory for objects:
course = new CS_Course; //Use the default constructor
or
//Use constructor with 2 parameters
course = new CS_Course(constructor_param1, constructor_param2);
Of course, if you don't need a pointer, you can (and should) create a CS_Course object like this (generally referred to as allocating on the stack):
CS_Course course; //default constructor
//constructor with 2 parameters
CS_Course course2(constructor_param1, constructor_param2);

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

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.