a nicer way to create structs in a loop - c++

I haven't coded in C++ in ages. And recently, I'm trying to work on something
involving structs. Like this
typedef struct{
int x;
int y;
} Point;
Then in a loop, I'm trying to create new structs and put pointers to them them in a list.
Point* p;
int i, j;
while (condition){
// compute values for i and j with some function...
p = new Point;
p* = {i, j}; //initialize my struct.
list.append(p); //append this pointer to my list.
}
Now, my question is it possible to simplify this? I mean, the pointer
variable *p outside of the loop and calling p = new Point inside the loop.
Isn't there a better/nicer syntax for this?

Sure:
Point * p = new Point;
You should probably also give your Point class a constructor:
struct Point { // note no need for typedef
int x;
int y;
Point( int ax, int ay ) : x( ax ), y( ay ) {}
};
so that you can say:
Point * p = new Point( i, j );
You may also want to make your list a list of Point values, rather than pointers, in which case you can avoid using dynamic allocation with new - always something to be avoided wherever possible in C++.

The struct can have a constructor like:
struct Point{
Point(int ax, int ay):x(ax), y(ay){}
int x;
int y;
};
and then the function can look like:
int i, j;
while (condition)
{
list.append(new Point(i,j));
}

As structs are classes with public members by default, you could even create a constructor within the struct and initialize your point object within the loop like this:
Point* p = new Point(i,j);

I would venture a guess that it is extremely unlikely you really need to allocate something like a Point dynamically.
Most likely you want to add a constructor and store them by value:
list<Point> list;
list.append(Point(x, y));

I recommend the Factory approach. Assuming that "Point" will be the base class of many objects, you can have a "Factory" that would return pointers.
Ex:
struct Point
{
Point(int mx, int my):x(mx),y(my) {}
int x;
int y;
};
// Circle, Polygon, etc.
class Factory
{
public:
static Point *getPoint(int mx, int my) { return new Point(mx, my); }
// Circle, Polygon, etc
};
Then in code someplace:
while(cond)
{
list.append(Factory::getPoint(i, j));
}

Related

How to make a class variable in one line?

In C++ how make a class variable in one line?
For example:
I have a class:
class point{
public:
int x;
int y;
};
How to make a variable in one line like java you can do new point(x, y), currently I do make a tmp and then push back to vector or something, are the simply way like java can do what I do in one line?
For creating a variable of type point on the stack you can use:
point myVariable{5,6};//this creates a point type variable on stack with x=5 and y=6;
So the complete program would look like:
#include <iostream>
class point{
public:
int x;
int y;
};
int main()
{
point myVariable{5,6};
return 0;
}
The output of the above program can be seen here.
If you want to create a vector of point objects and then add objects into it, then you can use:
//create point objects
point p1{5,6};
point p2{7,8};
//create a vector
std::vector<point> myVector;
//add p1 and p2 into the vector
myVector.push_back(p1);
myVector.push_back(p2);
Build a constructor Point(int x, int y) : x(x), y(y) {}
And then push to vector as usual vec.push_back(Point(x,y))

Is there an efficient way to initialise the size of a multidimensional vector when only known at runtime?

I have a class that has a 3D vector as one of it's variables. This size of this vector won't be known until runtime. Is there an efficient way to initialise this vector?
For example, my class may be
class Foo {
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
std::vector<std::vector<std::vector<float>>> bar;
int ni;
int nj;
int nk;
}
with a constructor
Foo::Foo(std::vector<std::vector<std::vector<float>>> x_,
std::vector<std::vector<std::vector<float>>> y_,
std::vector<std::vector<std::vector<float>>> z_) {
x = x_;
y = y_;
z = z_;
ni = x.size();
nj = x[0].size();
nk = x[0][0].size();
std::vector<std::vector<std::vector<float>>> tmp(ni, std::vector<std::vector<float>>(nj, std::vector<float>(nk)));
bar = tmp;
}
Can I do the last two lines of the above without having to assign the dummy variable tmp?
This is how you could do it (but don't miss to read the end):
#include <vector>
class Foo {
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
int ni;
int nj;
int nk;
using inner_type = std::vector<float>;
using middle_type = std::vector<inner_type>;
using outer_type = std::vector<middle_type>;
outer_type bar;
Foo(outer_type x_,
outer_type y_,
outer_type z_) :
x(x_),y(y_),z(z_),
ni(x.size()),
nj(ni ? x[0].size() : 0),
nk(nj ? x[0].size() : 0),
bar( outer_type(ni,middle_type(nj,inner_type(nk))))
{
}
};
Members are initialized before the constructor body is executed, thats why I used the member initializer list. And I changed the order of the members, because members are initialized in the order they appear in the class definition. The access to x[0] made me a bit nervous, so I tried to make sure empty vectors don't cause havoc.
This works and does what you want (I hope), but the vectors are populated with copies of the temporaries passed to their constructor, which isn't quite efficient. As an alternative you can resize the member as suggested in this answer.
Last not least, reconsider if you really want a std::vector<std::vector<std::vector<float>>>. If you need all "rows" to have same number of "columns" then a nested vector makes you pay for something you do not use. Moreover, the most attractive feature of std::vector is its memory-locality. Though, the floats in a std::vector<std::vector<float>> are stored in fragmented areas of memory (as the elements are not stored directly in the vector).
A flat std::vector<float> with appropriate index transformation is often the better choice.
float& access_element(size_t i, size_t j, size_t k) {
return bar[ i *offset_i + j*offset_j + k];
}
You could use resize() and a couple for loops to set bar. It's not the prettiest solution, but it should have pretty good performance as no temporaries are create and there are no unnecessary assignments. That would look like
bar.resize(ni);
for(auto& twodee : bar)
{
twodee.resize(nj);
for(auto& onedee : twodee)
onedee.resize(nk);
}
And now bar has the same size and is filled with zeros.

Address of local variable returned--can ignore warning without detriment but what's the proper way?

I have a class Particle:
class Particle {
private:
float x, y, z;
// ...
public:
// ...
float* getPos() {
float p[3] = {x, y, z};
return p;
}
// ...
};
I would call this method like:
Particle a = Particle();
// ...
float* pos = a.getPos();
And then reference the position elements with pos[0] through pos[2].
g++ spouts warning message as stated in the title. But the functionality is exactly how I want it: returning an array. Why does the warning exist and is there a "proper" way to do it?
You can't return a C-array like that, return std::array instead:
std::array<float, 3> getPos() {
std::array<float, 3> p = {x, y, z};
return p;
}
You'll need to include <array> for that.
Personally, I'd skip std::array/std::vector here, because in your particular case, the position of each value imposes independent meaning. In general, sequence types have ordering, tuples have structure; if the element count is fixed (and often heterogeneous) and sorting (or otherwise reordering the values) is intrinsically nonsensical (e.g. in the case of a coordinate, swapping the x and y values changes the meaning), then a tuple makes more sense.
In this case, you could just declare:
std::tuple<float, float, float> getPos() {
// C++17 or higher allows list initialization
return {x, y, z};
// Pre-C++17 you use the std::make_tuple helper
return std::make_tuple(x, y, z);
}
The advantage here is that you can then unpack the result in the caller easily, either with std::tie:
float x, y, z;
std::tie(x, y, z) = a.getPos();
or on C++17 or higher with structured bindings, it's even nicer, since you can declare and initialize the variables with auto, rather than declaring with explicit types, then reassigning with tie:
auto [x, y, z] = a.getPos();
You can store the tuple itself and use std::get if you prefer, but unpacking to useful names rather than obscure std::get indices usually makes for much cleaner code.
You're not returning an array. It's impossible to return an array in C++. You're returning a pointer to an array which no longer exists. Hence the warning.
You could make the array a part of your class and return a pointer to that. In general I wouldn't call that good design
class Particle {
private:
float pos[3];
// ...
public:
// ...
float* getPos() {
return pos;
}
// ...
};
You could return a vector<float> instead. You could return an array<float,3> instead. You could ask yourself why you need this.
p[3] will be destroyed when it goes out of scope so you shouldn't return a pointer to it.
Either return a std::array<float, 3> by value or consider making a class for positions too, and return a Position object, or a reference to it. Example:
struct Position {
float x, y, z;
};
class Particle {
private:
Position m_pos;
// ...
public:
// ...
Position const& getPos() const { return m_pos; }
// ...
};
I'd suggest that you're function is indicative of poor design. Provide getter methods an allow the user of your class to access member variables:
class Particle {
private:
float x, y, z;
public:
float GetX() const { return x; }
float GetY() const { return y; }
float GetZ() const { return z; }
};
Given const Particle a this will let you initialize an array as follows: const float pos[] = { a.GetX(), a.GetY(), a.GetZ() }
Creating a Particle method to allow the user to populate a float[] will encourage the user toward one of the following bad practices:
float* Particle::GetPos() const { return new[3]{ x, y, z }; } creates dynamic memory without clearly informing the caller that the memory needs to be released
array<float, 3U> Particle::GetPos() const { return { x, y, z }; } requires the allocation and creation of a temporary to populate a float[]
void Particle::GetPos(float* param) const { param[0] = x; param[1] = y; param[2] = z; } misses the opportunity for constant arrays and incurs potential caller misuse, as it's not clear that param must have room for at least 3 floats

Accessing a structure array through a pointer

I have a class with a structure for its position:
class thing
{
void setCoOrds(int, int, int);
string name;
struct location
{
int x;
int y;
int z;
} coOrd;
};
Then in a function I created an array of type thing.
int main()
{
thing * p_myThings = new thing[5];
// call array element here to use setCoOrds()
delete p_myThings;
return 0;
}
From the main function how would I access, lets say, thing element [3] so that I can use its .setCoOrds() function?
It should be:
p_myThings[3].setCoOrds
Also the
setCoOrds
is private by default which will not allow you to call the function.
int main()
{
thing * p_myThings = new thing[5];
p_myThings[3].setCoOrds(42,21,0);
delete[] p_myThings; // use delete[] for arrays btw
return 0;
}
I suppose that member function
void setCoOrds(int, int, int);
has public access control. In this case you can use the following constructions
p_myThings[3].setCoOrds( x, y, z );
or
( *( p_myThings + 3 ) ).setCoOrds( x, y, z );
or
( p_myThings + 3 )->setCoOrds( x, y, z );
You should use:
p_myThings[3].setCoOrds(x, y, z);
And for deleting pointer arrays you should use delete[] not delete

Create an array of an array of structs?

this question is regarding the syntax of an array of array of structs.
I have a struct that takes in two ints:
struct point
{
int x, y;
};
I have created another struct that takes in 8 of these structs:
//Creating an Array of Array of structs
struct Arraypoint
{
point variable[8];
};
//Not sure if this is the correct way to do it.
Now, in main, I want to declare an array variable of type Arraypoint with 8 indices, so effectively I will have 8 * 8 = 64 elements of struct point and 128 ints (64 x and 64 y).
Also, how would I access an individual element struct point from the array Arraypoint?
Okay after having declared in main lets say Arraypoint is 2.
Arraypoint arr[2];
How do I initialize the elements without having to type in arr[0].variable[0].x = ... or without using for loops.
Why can't I do the following, it doesn't seem to work.
Arraypoint arr[2] = { {(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y)},
{(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y)} }//xy are rand
I have used curly braces in my code, the error returned is missing braces around initializer for type point and too many initializers for type Arraypoint.
In C++, you'd just write:
Arraypoint arr[8];
An individual point could then be accessed via:
arr[i].variable[j];
More practically, though, you'd probably be better off using e.g.
std::vector<std::vector<point> >
or writing your own class with an overloaded operator(int i, int j). For example:
class PointMatrix
{
private:
std::vector<point> m_points;
public:
PointMatrix() : m_points(64) {}
point& operator()(int i, int j) { return m_points[8 * i + j]; }
const point& operator()(int i, int j) const { return m_points[8 * i + j]; }
};
PointMatrix mat;
m(3, 4).x = 23;
got it: ideone.com/ix3hC. Arraypoint::variable has to have it's own { } pair.
struct point
{
int x, y;
};
#define P {0, 0}
struct Arraypoint
{
point variable[8];
};
#define V { P, P, P, P, P, P, P, P}
#define AP { V } //this is the pair you missed
int main() {
Arraypoint arr[2] = { AP, AP };
}
struct Arraypoint arraypoints[8];
is what you're after, I think. To use them:
int firstx = arraypoints[0].variable[0].x;
This isn't so pretty though
struct point { int x, y; };
struct point[8][8] arraypoints;
Is probably better? Don't know what exactly you're after though.
To create an array of Arraypoints, you can do:
Arraypoint arr[8];
To access an element:
arr[i]
will return the i'th Arraypoint element
arr[i].variable[j]
will return the j'th point in the element
arr[i].variable[j].x
will return the x coordinate of that point.
So I realized why I couldn't declare my array as such,
Arraypoint arr[2] = { {(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y)},
{(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y),(x,y)} }
//xy are randomn integer values
its because in my struct declaration of Arraypoint, it takes in 8 elements of type point. So
I have to create variables of type point to store(x,y) and then i could store this variable in Array point.
point point1 = {x,y}, ...;
Arraypoint arr[2] = { {point1,point2,point3,point4,point5,....} };
Just for anyone in the future who stumbles across the same problem.