If I declare an array on the heap, how can I get information about the array?
Here is my code:
class Wheel
{
public:
Wheel() : pressure(32)
{
ptrSize = new int(30);
}
Wheel(int s, int p) : pressure(p)
{
ptrSize = new int(s);
}
~Wheel()
{
delete ptrSize;
}
void pump(int amount)
{
pressure += amount;
}
int getSize()
{
return *ptrSize;
}
int getPressure()
{
return pressure;
}
private:
int *ptrSize;
int pressure;
};
If I have the following:
Wheel *carWheels[4];
*carWheels = new Wheel[4];
cout << carWheels[0].getPressure();
How can I get call the .getPressure() method on any instance in the array when it is on the heap?
Also, if I want to create an array of Wheel on the heap, yet use this constructor when creating the array on the heap:
Wheel(int s, int p)
How do I do this?
Wheel *carWheels[4];
is an array of pointers to Wheel, so you need to initialize it with new:
for ( int i = 0; i < sizeof(carWheels)/sizeof(carWheels[0]); ++i)
carWheels[i]=new Wheel(); // or any other c-tor like Wheel(int s, int p)
later you can access it like that:
carWheels[0]->getPressure();
size of array can be retrieved like above:
sizeof(carWheels)/sizeof(carWheels[0])
[edit - some more details]
If you want to stick to array you will need to pass its size on function call because arrays decays to pointers then. You might want to stay with following syntax:
void func (Wheel* (arr&)[4]){}
which I hope is correct, because I never use it, but better switch to std::vector.
Also with bare pointers in arrays you must remember to delete them at some point, also arrays does not protect you against exceptions - if any will happen you will stay with memory leaks.
Simple, replace
Wheel *carWheels[4];
with
std::vector<Wheel*> carWheels(4);
for ( int i = 0 ; i < 4 ; i++ )
carWheels[i] = new Wheel(4);
You seem to be confusing () and [], I suggest you look into that.
You do know that ptrSize = new int(30); doesn't create an array, right?
Like C, you will need to lug the array's element count around with your allocation.
This information is actually stored by the implementation in some cases, but not in a way which is accessible to you.
In C++, we favor types such as std::vector and std::array.
Other notes:
ptrSize = new int(30); << creates one int with a value of 30
How do I do this?
Wheel(int s, int p)
Typically, you would just use assignment if you have an existing element:
wheelsArray[0] = Wheel(1, 2);
because you will face difficulty creating an array with a non-default constructor.
and while we're at it:
std::vector<Wheel> wheels(4, Wheel(1, 2));
is all that is needed to create 4 Wheels if you use vector -- no new required. no delete required. plus, vector knows its size.
Related
This question already has answers here:
How to resize array in C++?
(5 answers)
Closed 4 years ago.
I am sorry if this has already been covered before. I know how to do this is C and Java but not C++. Without using a pre-existing class which includes the use of Vector, how would you increase the size of an array given the code below?
The array expansion and assignment to the array takes place in push() noted with the all caps comment.
EDIT: As I have mentioned in comments below this is a question regarding manually reallocating arrays rather than using std::vector or "Dynamic Arrays."
Line.h
#include <iostream>
#include "Point.h"
using namespace std;
class Line {
public:
Line();
virtual ~Line();
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const Point& p);
private:
unsigned int index; // size of "points" array
Point* points;
};
Main.cpp
#include <iostream>
#include "Point.h"
#include "Line.h"
using namespace std;
int main() {
int x, y;
int size; // Some user defined size for the array
Line line;
Point a[size]; // Some points that are already filled
// Push the data in a[] to the variable "line"
for(int i = 0; i < size; i++){
// Increase array size of Point* points in variable line and add a[i] to the end of the array
line.push(points[i]);
}
return 0;
}
The simple answer is you should always use std::vector in this case. However it might be useful to explain just why that is. So lets consider how you would implement this without std::vector so you might see just why you would want to use std::vector:
// Naive approach
Line::push(const Point& p)
{
Point* new_points = new Points[index + 1];
std::copy(std::make_move_iterator(points), std::make_move_iterator(points+index), new_points);
new_points[index] = p;
delete[] points;
points = new_points;
index += 1;
}
This approach has many problems. We are forced to reallocate and move the entire array every time an entry is inserted. However a vector will pre-allocate a reserve and use space out of the reserve for each insert, only re-allocating space once the reserve limit is surpassed. This mean vector will far out perform your code in terms of performance as less time will be spent allocating and moving data unnecessarily. Next is the issue of exceptions, this implementation has no exception guarantees, where as the std::vector provides you with a strong exception guarantee: https://en.wikipedia.org/wiki/Exception_safety. Implementing a strong exception guarantee for your class is none trivial, however you would have automatically got this had you implemented this in terms of std::vector as such
Line::push(const Point& p)
{
points.push_back(p);
}
There are also other more subtle problems with your approach, your class does not define copy or assignment operators and so gets compiler generated shallow copy versions generated which means if someone copies your class then allocated members will get deleted twice. To resolve this you need to follow the rule of 3 paradigm pre C++11 and the rule of 5 for C++ 11 onwards: https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming). However had you used a vector none of this would be needed as you would benefit from the rule of zero and be able to rely on the compiler generated defaults: https://blog.rmf.io/cxx11/rule-of-zero
Essentially the only way is to use a dynamic array (one created using new[]) and to create an entirely new dynamic array and copy (or move) the objects from the old array to the new one.
Something like this:
class Line {
public:
Line(): index(0), points(nullptr) {} // initialize
virtual ~Line() { delete[] points; } // Clean up!
void push(const Point& p)
{
// create new array one element larger than before
auto new_points = new Point[index + 1];
// copy old elements to new array (if any)
for(unsigned int p = 0; p < index; ++p)
new_points[p] = points[p];
new_points[index] = p; // then add our new Point to the end
++index; // increase the recorded number of elements
delete[] points; // out with the old
points = new_points; // in with the new
}
private:
unsigned int index; // size of "points" array
Point* points;
};
But this approach is very inefficient. To do this well is quite complex. The main problems with doing things this way are:
Exception safety - avoiding a memory leak if an exception is thrown.
Allocation - avoiding having to reallocate (and re-copy) every single time.
Move semantics - taking advantage of some objects ability to be moved much more efficiently than they are copied.
A (slightly) better version:
class Line {
public:
Line(): index(0) {} // initialize
virtual ~Line() { } // No need to clean up because of `std::unique_ptr`
void push(const Point& p)
{
// create new array one element larger than before
auto new_points = std::unique_ptr<Point[]>(new Point[index + 1]);
// first add our new Point to the end (in case of an exception)
new_points[index] = p;
// then copy/move old elements to new array (if any)
for(unsigned int p = 0; p < index; ++p)
new_points[p] = std::move(points[p]); // try to move else copy
++index; // increase the recorded number of elements
std::swap(points, new_points); // swap the pointers
}
private:
unsigned int index; // size of "points" array
std::unique_ptr<Point[]> points; // Exception safer
};
That takes care of exception safety and (to some degree - but not entirely) move semantics. However it must be pointed out that exception safety is only going to be complete if the elements stored in the array (type Point) are themselves exception safe when being copied or moved.
But this does not deal with efficient allocation. A std::vector will over allocate so it doesn't have to do it with every new element. This code also misses a few other tricks that a std::vector would employ (like allocating uninitialized memory and constructing/destructing the elements manually as and when they are needed/discarded).
You basically have no way but to allocate a new array, copy existing values inside and delete [] the old one. That's why vector is doing the reallocation by a multiplicative factor (say each reallocation doubles the size). This is one of the reasons you want to use the standard library structures instead of reimplementing.
Keep It Simple
In my opinion, in this case, it's better to use a Linked-List of CPoint in CLine:
struct CPoint
{
int x = 0, y = 0;
CPoint * m_next = nullptr;
};
class CLine
{
public:
CLine() {};
virtual ~CLine()
{
// Free Linked-List:
while (m_points != nullptr) {
m_current = m_points->m_next;
delete m_points;
m_points = m_current;
}
};
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const CPoint& p)
{
m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = new CPoint);
m_current->m_x = p.m_x;
m_current->m_y = p.m_y;
m_index++;
};
private:
unsigned int m_index = 0; // size of "points" array
CPoint * m_points = nullptr, * m_current = nullptr;
};
.
Or, even better with smart pointers:
#include <memory>
struct CPoint
{
int m_x = 0, m_y = 0;
std::shared_ptr<CPoint> m_next;
};
class CLine
{
public:
CLine() {};
virtual ~CLine() {}
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const CPoint& p)
{
m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = std::make_shared<CPoint>());
m_current->m_x = p.m_x;
m_current->m_y = p.m_y;
m_index++;
};
private:
unsigned int m_index = 0; // size of "points" array
std::shared_ptr<CPoint> m_points, m_current;
};
I have a class that contains several arrays whose sizes can be determined by parameters to its constructor. My problem is that instances of this class have sizes that can't be determined at compile time, and I don't know how to tell a new method at run time how big I need my object to be. Each object will be of a fixed size, but different instances may be different sizes.
There are several ways around the problem:- use a factory- use a placement constructor- allocate arrays in the constructor and store pointers to them in my object.
I am adapting some legacy code from an old application written in C. In the original code, the program figures out how much memory will be needed for the entire object, calls malloc() for that amount, and proceeds to initialize the various fields.
For the C++ version, I'd like to be able to make a (fairly) normal constructor for my object. It will be a descendant of a parent class, and some of the code will be depending on polymorphism to call the right method. Other classes descended from the same parent have sizes known at compile time, and thus present no problem.
I'd like to avoid some of the special considerations necessary when using placement new, and I'd like to be able to delete the objects in a normal way.
I'd like to avoid carrying pointers within the body of my object, partially to avoid ownership problems associated with copying the object, and partially because I would like to re-use as much of the existing C code as possible. If ownership were the only issue, I could probably just use shared pointers and not worry.
Here's a very trimmed-down version of the C code that creates the objects:
typedef struct
{
int controls;
int coords;
} myobject;
myobject* create_obj(int controls, int coords)
{
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
char* mem = malloc(size);
myobject* p = (myobject *) mem;
p->controls = controls;
p->coords = coords;
return p;
}
The arrays within the object maintain a fixed size of the life of the object. In the code above, memory following the structure of myobject will be used to hold the array elements.
I feel like I may be missing something obvious. Is there some way that I don't know about to write a (fairly) normal constructor in C++ but be able to tell it how much memory the object will require at run time, without resorting to a "placement new" scenario?
How about a pragmatic approach: keep the structure as is (if compatibility with C is important) and wrap it into a c++ class?
typedef struct
{
int controls;
int coords;
} myobject;
myobject* create_obj(int controls, int coords);
void dispose_obj(myobject* obj);
class MyObject
{
public:
MyObject(int controls, int coords) {_data = create_obj(controls, coords);}
~MyObject() {dispose_obj(_data);}
const myobject* data() const
{
return _data;
}
myobject* data()
{
return _data;
}
int controls() const {return _data->controls;}
int coords() const {return _data->coords;}
double* array() { return (double*)(_data+1); }
private:
myobject* _data;
}
While I understand the desire to limit the changes to the existing C code, it would be better to do it correctly now rather than fight with bugs in the future. I suggest the following structure and changes to your code to deal with it (which I suspect would mostly be pulling out code that calculates offsets).
struct spots
{
double x;
double y;
};
struct myobject
{
std::vector<double> m_controls;
std::vector<spots> m_coordinates;
myobject( int controls, int coordinates ) :
m_controls( controls ),
m_coordinates( coordinates )
{ }
};
To maintain the semantics of the original code, where the struct and array are in a single contigious block of memory, you can simply replace malloc(size) with new char[size] instead:
myobject* create_obj(int controls, int coords)
{
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
char* mem = new char[size];
myobject* p = new(mem) myobject;
p->controls = controls;
p->coords = coords;
return p;
}
You will have to use a type-cast when freeing the memory with delete[], though:
myobject *p = create_obj(...);
...
p->~myobject();
delete[] (char*) p;
In this case, I would suggest wrapping that logic in another function:
void free_obj(myobject *p)
{
p->~myobject();
delete[] (char*) p;
}
myobject *p = create_obj(...);
...
free_obj(p);
That being said, if you are allowed to, it would be better to re-write the code to follow C++ semantics instead, eg:
struct myobject
{
int controls;
int coords;
std::vector<double> values;
myobject(int acontrols, int acoords) :
controls(acontrols),
coords(acoords),
values(acontrols + acoords*2)
{
}
};
And then you can do this:
std::unique_ptr<myobject> p = std::make_unique<myobject>(...); // C++14
...
std::unique_ptr<myobject> p(new myobject(...)); // C++11
...
std::auto_ptr<myobject> p(new myobject(...)); // pre C++11
...
New Answer (given comment from OP):
Allocate a std::vector<byte> of the correct size. The array allocated to back the vector will be contiguous memory. This vector size can be calculated and the vector will manage your memory correctly. You will still need to be very careful about how you manage your access to that byte array obviously, but you can use iterators and the like at least (if you want).
By the way here is a little template thing I use to move along byte blobs with a little more grace (note this has aliasing issues as pointed out by Sergey in the comments below, I'm leaving it here because it seems to be a good example of what not to do... :-) ) :
template<typename T>
T readFromBuf(byte*& ptr) {
T * const p = reinterpret_cast<T*>(ptr);
ptr += sizeof(T);
return *p;
}
Old Answer:
As the comments suggest, you can easily use a std::vector to do what you want. Also I would like to make another suggestion.
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
The above line of code suggests to me that you have some "hidden structure" in your code. Your myobject struct has two int values from which you are calculating the size of what you actually need. What you actually need is this:
struct ControlCoord {
double control;
std::pair<double, double> coordinate;
};
std::vector<ControlCoord>> controlCoords;
When the comments finally scheded some light on the actual requirements, the solution would be following:
allocate a buffer large enough to hold your object and the array
use placement new in the beginning of the buffer
Here is how:
class myobject {
myobject(int controls, int coords) : controls(controls), coords(coords) {}
~myobject() {};
public:
const int controls;
const int coords;
static myobject* create(int controls, int coords) {
std::unique_ptr<char> buffer = new char[sizeof(myobject) + (controls + coords*2) * sizeof(double)];
myobject obj* = new (buffer.get()) myobject(controls, coords);
buffer.release();
return obj;
}
void dispose() {
~myobject();
char* p = (char*)this;
delete[] p;
}
};
myobject *p = myobject::create(...);
...
p->dispose();
(or suitably wrapped inside deleter for smart pointer)
Basically I want to create an array of objects with a size which is passed through from one class to another i.e.
Object * ArrayOfObjects = new Object[Size];
Whilst that creates an array successfully, it doesnt allow me to use constructors.
How can I create an array of my objects then define each object in the array?
Once you allocate memory for the array you can then assign to it by looping:
for (int i = 0; i < Size; ++i)
{
ArrayOfObjects[i] = Object( /* call the constructor */ );
}
Or you can use a vector to do the same but with more ease of use:
std::vector<Object> ArrayOfObjects = { Object(...), Object(...) };
What you're asking may not actually be the best thing to do - which would be to use something like std::vector or something, but under the hood what they're going to do is what your question asks about anyway.
Then you can either assign or placement new each entry:
for (size_t i = 0; i < Size; ++i)
{
// Option 1: create a temporary Object and copy it.
ArrayOfObjects[i] = Object(arg1, arg2, arg3);
// Option 2: use "placement new" to call the instructor on the memory.
new (ArrayOfObjects[i]) Object(arg1, arg2, arg3);
}
Once you allot memory, as you did. You initialize each object by traversing the array of objects
and call its constructor.
#include<iostream>
using namespace std;
class Obj
{
public:
Obj(){}
Obj(int i) : val(i)
{
cout<<"Initialized"<<endl;
}
int val;
};
int allot(int size)
{
Obj *x= new Obj[size];
for(int i=0;i<10;i++)
x[i]=Obj(i);
//process as you need
...
}
I have these scenarios and I want to know if I manage my memory correctly. I watch the memory consumption in the Task Manager when I start the executable and see how memory is not popped back to the initial amount, which leads me to suspect that I don't clear memory where is needed.
So, in this first case I have a function that adds a new element to a dynamic array:
struct Color {
int R;
int G;
int B;
}
int TotalColors;
Color* Rainbow;
void AddColor(Color NewColor) {
// So, I create a new array of size TotalColors+1
Color* NewRainbow = new Color[TotalColors+1];
// Now I add the existing elements
for (int i=0; i<TotalColors; i++) {
NewRainbow[i] = Rainbow[i];
}
// And lastly, I add the new element
NewRainbow[TotalColors] = NewColor;
// Now, I assign the NewRainbow to Rainbow (I don't know if it's correct)
Rainbow = NewRainbow;
}
So, in this case, do you think I miss something? This is working but I want to make sure the unused stuff is removed from memory.
I also have a function to remove an element, which looks like this:
void RemoveColor(Color Removable) {
// Again, I create a new array of size TotalColors-1
Color* NewRainbow = new Color[TotalColors-1];
// I scan the list and add only those elements which are not 'Removable'
for (int i=0; i<TotalColors; i++) {
// Let's suppose that Removable exists in the list
if (Rainbow[i].R != Removable.R && Raibow[i].G != Removable.G && ... {
NewRainbow [i] = Rainbow[i];
}
}
// Again, the same operation as above
NewRainbow[TotalColors] = NewColor;
Rainbow = NewRainbow;
}
In this case, I don't know what happens with Rainbow[Removable], I mean, the element of the array that is removed.
And the last case, is this, where I try to send the pointer of an element from the array to a function.
Color* GetColor(int Index) {
Color* FoundColor;
// Scan the array
for (int i=0; i<TotalColors; i++) {
if (i == Index) FoundColor = &Rainbow[i];
}
return FoundColor;
}
// And I use it like this
void ChangeColor(int Index) {
Color* Changeable;
Changeable = GetColor(Index);
SetRGB(Changeable, 100, 100, 100);
}
// And this is what changes the value
void SetRGB(Color* OldRGB, int R, int G, int B) {
(*oldRGB).R = R;
(*oldRGB).G = G;
(*oldRGB).B = B;
}
And this is it. So, this works but I am not sure if with so many pointers I didn't forget to delete something. For example, when I RemoveColor I don't see the memory changed (maybe some bytes don't make the difference) and I just want some professional eye to tell me if I missed something. Thanks!
In the first function AddColor() you are not deleting the previously allocated memory.
Rainbow = NewRainbow; // leaking the memory Rainbow was previously pointing to.
Change that last line to:
delete[] Rainbow;
Rainbow = NewRainbow;
Same thing with RemoveColor()
Any time you use the new operator it needs to have a corresponding delete. Also, if you are allocating an array with new[] as in your case, it must have a corresponding delete[].
In order not to worry whether you've forgotten to delete a pointer, you shouldn't use plain pointers. Instead, use smart pointers such as
std::shared_ptr
std::unique_ptr
etc.
or, if you don't have C++11 yet, use
boost::shared_ptr
boost::scoped_ptr
More on smart pointers, see Wikipedia and the specific documentation.
I have a structure which includes a string field. I create an array of those structures and then I want to pass them to a function (by reference). Everything works perfectly fine when I comment out the string field, but if I don't the program crashes. I can't find an answer to this anywhere..
Here's the code (I reduced it to only show the issue):
struct student {
int a;
int b;
string name[20];
char status;
};
void operation(student the_arr[1],int number_of_students) {
delete[] the_arr;
the_arr = new student[3];
for(int i = 0; i<3; i++) {
the_arr[i].a = i+5;
the_arr[i].b = i+4;
}
}
int main() {
student *abc;
abc = new student[0];
operation(abc, 0);
system("pause");
return 0;
}
I need the array to be dynamic so I can change its' size when I need to.
Assuming you can't use std::vector instead of dynamically allocated arrays follow the answer below. In any other case you should use the containers provided by the standard library.
Note: Your program doesn't crash. The only things the compiler will complain about it the allocating zero elements part, but will let you compile and run this program.
Your function is completely wrong. When using dynamic allocation you can simply pass a pointer like this:
void operation(student* the_arr, int number_of_students) {
Then inside your function you are dynamically allocating memory which is stored inside the the_arr pointer which is not passed by reference therefore leading to the creation of a local pointer variable that will lose the pointer after its execution:
void operation(student*& the_arr [...]
I suggest you to avoid the below solution though and return the new pointer instead:
student* operation(student* the_arr, int number_of_students) {
delete[] the_arr;
the_arr = new student[3];
[...]
return the_arr; // <----
}
Allocating abc = new student[0]; doesn't make any sense. You are trying to allocate an array of 0 elements. Maybe you meant abc = new student[1];?
You should just use the vector or other sequence objects. Though I'm not sure what you are trying to do with your code. Here's a quick example:
// Vector represent a sequence which can change in size
vector<Student*> students;
// Create your student, I just filled in a bunch of crap for the
// sake of creating an example
Student * newStudent = new Student;
newStudent->a = 1;
newStudent->b = 2;
newStudent->name = "Guy McWhoever";
newStudent->status = 'A';
// and I pushed the student onto the vector
students.push_back( newStudent );
students.push_back( newStudent );
students.push_back( newStudent );
students.push_back( newStudent );