It was suggested to me to use pointers to add a vector that I wanted to pass from some existing function to another function. I am really stuck on how to get the information back out of that pointer though. I've tried a number of things I've read here and there so let me demonstrate what I'm talking about.
primary program:
std::vector<float> * dvertex=NULL;
track.calculate(irrelevant stuff, dvertex)
secondary program (track, calculate)
track::caclulate(irrelevant stuff, vector<float> * dvertex)
{
...
vector<float> pos;
... pos filled after some calculations
if(! (dvertex==NULL))
{
dvertex = &pos1;
}
back to primary, unless I messed up something above, here's some things I've tried
1
(*dvertex).at(0)
float z = (*dvertex).at(0)
2
(*dvertex)[0]
and a bunch of stuff that just plain didn't compile. I'm quite stuck as I'm not sure how to get the specific values out of that vector in the main program. I even thought it might be the if(! (dvertex==NULL)) bit, so I changed it to if(dvertex==NULL) but still no joy. Any help would be greatly appreciated.
*Edit/Update*Thanks so much everyone for the help, but I fear I'm still doing it wrong.
So following the suggestions that I just pass a reference: I did this:
primary
std::vector<float> dvertex;
track.calculate( foo, &dvertex);
secondary stayed the same (with !Null check)
primary
std::cout<<dvertex[0]<<std:endl;
(among other attempts to actually use the data)
Thanks a lot for any thoughts on what I'm still doing improperly. Everything compiles, the program just freezes when it gets to a point that the data from dvertex is used.
Edit:Final fix
in the secondary program I needed
*dvertex = pos1;
instead of
dvertex = &pos1;
I'm not sure why these didn't compile for you, because they're valid as long as the pointer is valid and not null:
void f(std::vector<int>* v)
{
if( v != 0 ) {
int n = (*v)[0]; // ok
int m = (*v).at(0); // ok
int o = v->at(0); // ok
}
}
But never mind that. Use a reference if you must change the vector, and a const reference if you must not. There's rarely if ever a need to take a container by pointer.
Also, I suggest you check pointers against 0, not NULL, because sometimes NULL is defined as (void*)0 as per C compilers. But some people may argue otherwise here.
If you're going to modify the vector, you probably just want to pass it by reference. If you do use a pointer, however, you need to define a vector in main, and then pass the address of that vector:
void calculate(std::vector<float> *vertex_array) {
vertex_array->pushback(1.0f);
vertex_array->pushback(2.0f);
}
int main() {
std::vector<float> vertexes;
calculate(&vertexes);
std::copy(vertexes.begin(), vertexes.end(),
std::ostream_iterator<float>(std::cout, "\n"));
return 0;
}
See my note above, but for your scenario to work, you need
std::vector<float> * dvertex=NULL;
to be
std::vector<float> * dvertex = new std::vector<float>();
Related
Sorry for the confusing title, and sorry if this is a duplicate (I tried searching for an answer online), hopefully this example clears it up:
Basically, would it be better to do this:
void fill(vector<int> & v);
int main() {
vector<int> v;
fill(v);
return 0;
}
or this:
vector<int> fill();
int main() {
vector<int> v = fill();
return 0;
}
I've been reading about how in C++11 the compiler will move the return results of functions rather than copying them. Is one of these better than the other? Or is it simply preference?
From the perspective of Single responsibility principle it is better to use return value for modifying an variable.
For example if you have a fill() function that creates initial configuration for a vector, the function's responsibility is to create initial configurations(read them from db, file or just hard-coded configuration). Not assign the configuration to your variable(that might be also a complex process).
And if you are going to change only 1 variable, it increments the readability of the code. You don't need to remember if the fill() function modifies your variable or not.
The former is better as it avoids a copy operation at the end . But I rather prefer this :
bool fill(vector<int> & v);
The return flag is used to identify if the function was executed successfully.
I have a struct and two vectors in my .h file:
struct FTerm {
int m_delay;
double m_weight;
};
std::vector<FTerm> m_xterms;
std::vector<FTerm> m_yterms;
I've already read in a file to populate values to m_xterms and m_yterms and I'm trying to iterate through those values:
vector<FTerm>::iterator terms;
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
{
int delaylength = m_xterms->m_delay * 2; // Assume stereo
double weight = m_xterms->m_weight;
}
Although I'm pretty sure I have the logic wrong, I currently get the error Error expression must have a pointer type. Been stuck at this for a while, thanks.
Change
int delaylength = m_xterms->m_delay * 2;
double weight = m_xterms->m_weight;
to
int delaylength = terms->m_delay * 2;
// ^^^^^
double weight = terms->m_weight;
// ^^^^^
as you want to access values through
vector<FTerm>::iterator terms;
within the loop
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
// ^^^^^
"Although I'm pretty sure I have the logic wrong, ..."
That can't be answered, unless you give more context about the requirements for the logic.
Along with the problem πάντα ῥεῖ pointed out, your code currently has a problem that it simply doesn't accomplish anything except wasting some time.
Consider:
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
{
int delaylength = m_xterms->m_delay * 2; // Assume stereo
double weight = m_xterms->m_weight;
}
Both delaylength and weight are created upon entry to the block, and destroyed on exit--so we create a pair of values, then destroy them, and repeat for as many items as there are in the vector--but never do anything with the values we compute. They're just computed, then destroyed.
Assuming you fix that, I'd also write the code enough differently that this problem simply isn't likely to happen to start with. For example, let's assume you really wanted to modify each item in your array, instead of just computing something from it and throwing away the result. You could do that with code like this:
std::transform(m_xterms.begin(), m_xterms.end(), // Source
m_xterms.begin(), // destination
[](FTerm const &t) { return {t.m_delay * 2, t.m_weight}; });// computation
Now the code actually accomplishes something, and it seems a lot less likely that we'd end up accidentally writing it incorrectly.
Bottom line: standard algorithms are your friends. Unlike human friends, they love to be used.
I once landed an interview and was asked what the purpose of assigning a variable by reference would be (as in the following case):
int i = 0;
int &j = i;
My answer was that C++ references work like C pointers, but cannot assume the NULL value, they must always point to a concrete object in memory. Of course, the syntax is different when using references (no need for the pointer indirection operator, and object properties will be accessed via the dot (.) rather than the arrow (->) operator). Perhaps the most important difference, is that unlike with pointers, where you can make a pointer point to something different (even after it was pointing to the same thing as another pointer), with references, if one reference is updated, then the other references which pointed to the same thing are also updated to point to the very same object.
But then I went on to say that the above use of references is pretty useless (and perhaps this is where I went wrong), because I couldn't see a practical advantage to assigning by reference: since both references end up pointing to the same thing, you could easily do with one reference, and couldn't think of a case where this wouldn't be the case. I went on to explain that references are useful as pass-by-reference function parameters, but not in assignments. But the interviewer said they assign by reference in their code all the time, and flunked me (I then went on to work for a company that this company was a client of, but that's besides the point).
Anyways, several years later, I would like to know where I could have gone wrong.
To begin with, I'd hope for that company's sake that wasn't the ONLY reason they didn't hire you, since it's a petty detail (and no, you don't really know exactly why a company doesn't hire you).
As touched on in the comment, references NEVER change what they refer to within their lifetime. Once set, a reference refers to that same location, until it "dies".
Now, references are quite useful to simplify an expression. Say we have a class or structure with a fair amount of complicated content. Say something like this:
struct A
{
int x, y, z;
};
struct B
{
A arr[100];
};
class C
{
public:
void func();
B* list[20];
};
void C::func()
{
...
if (list[i]->arr[j].x == 4 && list[i]->arr[j].y == 5 &&
(list[i]->arr[j].z < 10 || list[i]->arr[j].z > 90))
{
... do stuff ...
}
}
That's a lot of repeats of list[i]->arr[j] in there. So we could rewrite it using a reference:
void C::func()
{
...
A &cur = list[i]->arr[j];
if (cur.x == 4 && cur.y == 5 &&
(cur.z < 10 || cur.z > 90))
{
... do stuff ...
}
}
The above code assumes do stuff is actually mofidying the cur element in some way, if not, you should probably use const A &cur =... instead.
I use this technique quite a bit to make it clearer and less repetitive.
In this particular case of assigning a reference to a local variable of primitive type in the same scope, the assignment is very much useless: there is nothing you can do using j that you could not do using i. There are several mildly negative consequences to it, too, because the readability would suffer, and the optimizer may get confused.
Here is one legitimate use of assigning a reference:
class demo {
private:
map<int,string> cache;
string read_resource(int id) {
string resource_string;
... // Lengthy process for getting a non-empty resource string
return resource_string;
}
public:
string& get_by_id(int id) {
// Here is a nice trick
string &res = cache[id];
if (res.size() == 0) {
// Assigning res modifies the string in the map
res = read_resource(id);
}
return res;
}
};
Above, variable res of reference type refers to an element of the map that is either retrieved, or created new. If the string is created new, the code calls the "real" getter, and assigns its result to res. This automatically updates the cache, too, saving us another lookup in the cache map.
I am trying to create a jump table for a fuzzy controller. Basically, I have a lot of functions that take in a string and return a float, and I want to be able to do something along the lines:
float Defuzzify(std::string varName, DefuzzificationMethod defuzz)
{
return functions[defuzz](varName);
}
where DefuzzificationMethod is an enum. The objective is to avoid a switch statement and have a O(1) operation.
What I have right now is:
float CenterOfGravity(std::string varName);
std::vector<std::function<float (std::string)>> defuzzifiers;
Then I try to initialize it in the constructor with:
defuzzifiers.reserve(NUMBER_OF_DEFUZZIFICATION_METHODS);
defuzzifiers[DEFUZZ_COG] = std::bind(&CenterOfGravity, std::placeholders::_1);
This is making the compiler throw about 100 errors about enable_if (which I don't use anywhere, so I assume std does). Is there a way to make this compile ? Moreover, is there a way to make this a static vector, since every fuzzy controller will essentially have the same vector ?
Thanks in advance
Reserve just makes sure there's enough capacity, it doesn't actually mak the vector's size big enough. What you want to do is:
// construct a vector of the correct size
std::vector<std::function<float (std::string)>> defuzzifiers(NUMBER_OF_DEFUZZIFICATION_METHODS);
// now assign into it...
// if CentorOfGravity is a free function, just simple = works
defuzzifiers[DEFUZZ_COG] = CenterOfGravity;
// if it's a method
defuzzifiers[DEFUZZ_COG] = std::bind(&ThisType::CenterOfGravity, this, std::placeholders::_1);
Now this might leave you some holes which don't actually have a function defined, so maybe you want to provide a default function of sorts, which the vector constructor allows too
std::vector<std::function<float (std::string)>> defuzzifiers(
NUMBER_OF_DEFUZZIFICATION_METHODS,
[](std::string x) { return 0f; }
);
An unrelated note, you probably want your functions to take strings by const-ref and not by value, as copying strings is expensive.
Should I be worried about having too many levels of vectors in vectors?
For example, I have a hierarchy of 5 levels and I have this kind of code
all over my project:
rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks[pos.d]
where each element is a vector. The whole thing is a vector of vectors of vectors ...
Using this still should be lot faster than copying the object like this:
Block b = rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks[pos.d];
// use b
The second approach is much nicer, but slower I guess.
Please give me any suggestion if I should worry about performance issues related to this,
or else...
Thanks
Efficiency won't really be affected in your code (the cost of a vector random access is basically nothing), what you should be concerned with is the abuse of the vector data structure.
There's little reason that you should be using a vector over a class for something as complex as this. Classes with properly defined interfaces won't make your code any more efficient, but it WILL make maintenance much easier in future.
Your current solution can also run into undefined behaviour. Take for example the code you posted:
Block b = rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks[pos.d];
Now what happens if the vector indexes referred to by pos.a, pos.b, pos.c, pos.d don't exist in one of those vectors? You'll go into undefined behaviour and your application will probably segfault (if you're lucky).
To fix that, you'll need to compare the size of ALL vectors before trying to retrieve the Block object.
e.g.
Block b;
if ((pos.a < rawSheets.size()) &&
(pos.b < rawSheets[pos.a].countries.size()) &&
(pos.c < rawSheets[pos.a].countries[pos.b].cities.size()) &&
(pos.d < rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks.size()))
{
b = rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks[pos.d];
}
Are you really going to do that every time you need a block?!!
You could do that, or you can, at the very least, wrap it up in a class...
Example:
class RawSheet
{
Block & FindBlock(const Pos &pos);
std::vector<Country> m_countries;
};
Block & RawSheet::FindBlock(const Pos &pos)
{
if ((pos.b < m_countries.size()) &&
(pos.c < m_countries[pos.b].cities.size()) &&
(pos.d < m_countries[pos.b].cities[pos.c].blocks.size()))
{
return m_countries[pos.b].cities[pos.c].blocks[pos.d];
}
else
{
throw <some exception type here>;
}
}
Then you could use it like this:
try
{
Block &b = rawSheets[pos.a].FindBlock(pos);
// Do stuff with b.
}
catch (const <some exception type here>& ex)
{
std::cout << "Unable to find block in sheet " << pos.a << std::endl;
}
At the very least, you can continue to use vectors inside the RawSheet class, but with it being inside a method, you can remove the vector abuse at a later date, without having to change any code elsewhere (see: Law Of Demeter)!
Use references instead. This doesn't copy an object but just makes an alias to make it more usable, so performance is not touched.
Block& b = rawSheets[pos.a].countries[pos.b].cities[pos.c].blocks[pos.d];
(watch the ampersand). When you use b you will be working with the original vector.
But as #delnan notes you should be worried more about your code structure - I'm sure you could rewrite it in a more appropriate and maintable way.
You should be worried about specific answers since we don't know what the constraints are for your program or even what it does?
The code you've given isn't that bad given what little we know.
The first and second approaches you've shown are functionally identical. Both by default will return an object reference but depending on assignment may result in a copy being made. The second certainly will.
Sasha is right in that you probably want a reference rather than a copy of the object. Depending on how you're using it you may want to make it const.
Since you're working with vectors, each call is fixed time and should be quite fast. If you're really concerned, time the call and consider how often the call is made per second.
You should also consider the size of your dataset and think about if another data structure (database perhaps) would be more appropriate.