I'm pretty new to C++, and I'm finding the whole concept of pointers, double pointers and references some-what confusing.
I am writing an object loader for an assignment and have came to the part where I want to optimise/modularise my solution.
Right now I have 3 vectors which contain information regarding the objects texture coords, faces and normals. Instead of having an operation for each, I wish to neaten my codebase by introducing a method to handle pushing to the following vectors.
std::vector<XMFLOAT3> vert_texture_coord;
std::vector<XMFLOAT3> vert_normals;
std::vector<XMFLOAT3> vert_position;
Currently I write to them like this:
vert_text_coord.push_back(XMFLOAT3(vert_x, vert_y, vert_z));
But to modularise I have written a method:
push_to_vector(float x, float y, float z, *vector)
{
// push code here
}
Calling it like this
push_to_vector(vert_x, vert_y, vert_z, &vert_text_coord);
Am I right to be passing the reference of vert_text_coord to the pointer parameter *vector in my push_to_vector method, or am I doing this wrong? Finally would it also make sense to have parameters vert_x, vert_y, vert_z as references too or have I completely misunderstood the concept of &?
Thanks in advance.
Actually, I think you are asking the wrong question here. Yes, you can perfectly well pass a pointer/reference to a function that pushes an XMFLOAT3 to the end of a vector, the working code would be
//function signature:
push_to_vector(std::vector<XMFLOAT3>* v, float x, float y, float z);
//call:
push_to_vector(&ver_normals, x, y, z);
or using references
//function signature:
push_to_vector(std::vector<XMFLOAT3>& v, float x, float y, float z);
//call:
push_to_vector(ver_normals, x, y, z);
However, as I said, that answers the wrong question. The right question would be: Is the idea of a push_to_vector() function a good one? And I believe, it is not. The reason is, that a push_to_vector() function is the wrong abstraction. Code that uses your three vectors will never want to abstract from which vector it uses, it will want to abstract from the fact that it uses a vector.
It is bad to have too long functions, but it is also bad to have tons of one-line functions like a push_to_vector() function. Each function should strive to have a sufficiently large difference in abstraction level between what it uses and what it provides. If that is not the case, you'll get lost in the deep call hierarchies that you will create.
(It is no accident, that the International Obfuscated C Code Contest has winning entries that either fuse everything into one function, or that have something like 50 functions, each of which are only a few characters long. Either method is equally efficient at obfuscating the code.)
Here is my two cents to the question whether it is better to use pointers or references:
Consider the following five functions:
void foo(int x);
void bar(int& x);
void baz(int* x);
void bim(const int& x);
void bam(const int* x);
and their corresponding calls:
int var = 7;
foo(var); //may not change var
bar(var); //may change var
baz(&var); //may change var
bim(var); //may not change var
bam(&var); //may not change var
The first call is the normal case in C++, and it cannot change its argument as it uses pass by value. I believe it is a really good idea, if you can see directly at the call whether the call will change its argument. Thus I restrict myself to either use pass by value, pass by pointer, or pass by const reference, i. e. these three variants:
foo(var); //may not change var
baz(&var); //may change var, visible by the take-address operator
bim(var); //may not change var, pass by value semantics optimized via a const reference
& takes the address of the object, so when you write &vert_text_coord you get a vector*
To pass by reference, you need to have the reference parameter on the function.
push_to_vector(float x, float y, float z, vector& vec)
{
vec.push_back(x,y,z);
}
You can then call the function like normal.
push_to_vector(vert_x, vert_y, vert_z, vert_text_coord);
So far it looks like you have the right idea.
A correction: a pointer parameter in a function looks like T*, not *T. So your push_to_vector fucntion would be like:
push_to_vector(float x, float y, float z, std::vector<XMFLOAT3>* v)
Also, unless you plan on changing x, y, and z, there is no need to pass them by reference.
EDIT:
As cocarin's answer says, passing by reference here is really the right way to do it.
Also as a side note, this sounds like it might be a good idea to wrap all of your vectors into a class. That way you don't have to be throwing these vectors around in functions a lot.
Learn to declare function parameters correctly.
As your function imitates method push_back that is written like
vert_text_coord.push_back(XMFLOAT3(vert_x, vert_y, vert_z));
then the first parameter should be reference to vector. For example
void push_to_vector( std::vector<XMFLOAT3> &, float, float, float );
In this case you can for example declare some parameters as having default arguments. For example
void push_to_vector( std::vector<XMFLOAT3> &, float = 0.0f, float = 0.0f, float = 0.0f );
And the function can be called like
push_to_vector( vert_text_coord, vert_x, vert_y, vert_z );
Or if you will declare some default arguments then like
push_to_vector( vert_text_coord );
You even can change default arguments of the function by means of redeclaring the function in a given (for example block) scope.
Also there is no sense to declare parameters of type float as references.
You could use a default argument for the last parameter as a mark that inside the function there should be used only two parameters.
void push_to_vector( std::vector<XMFLOAT3> &, float, float, float = 0.0f );
The function could be called either like
push_to_vector( vert_text_coord, vert_x, vert_y, vert_z );
or
push_to_vector( vert_text_coord, vert_x, vert_y );
In the last case vert_z woul be equal to 0.0f (or some other value) that would mean that the function itself should supply a required value.
Related
We got practice sheets for a test next week, for studying a little bit of C++ (still a beginner here). I still can't figure out a simple question.
That is, why are these snippets of code problematic?
int& p(int z) { return z; }
int* h(int z) { return &z; }
When int *h(int z) {return &z} is called, the parameter passed to the function is copied to a variable named z. That copy only lasts as long as the function. So once the function returns, it is no longer available to your program. So you can't have a valid pointer to it once the function returns: formally &z is invalidated.
The same is true for the reference version int &p(int z) {return z}.
As an exercise, see if you can figure out what would happen if z was itself a reference: i.e. int &p(int& z) {return z}. Then, a copy would not be taken. But do note that no professional would ever write a function like this.
The behavior of the returned value is undefined.The tricky thing here is that if you test your function with some kind of operation ( eg. printing to std output or assertion) you might often get an expected result but that doesn't mean it is safe since returned value is pointing to value written in the stack which could be wiped out at any given moment right after the function returns ( it is called stack unwinding). So, the rule of thumb is, don't return the address or reference to a locally defined variable of a function unless it was defined as static and why on earth would one prefer that unless situations force you? :-)
Both snippets pass a reference (or pointer in the second case) to temporary. Consider for example p(18). Where would 18 actually be stored? So where should p(18) point to?
Side note: If everything would be const, the code would be ok, ie.
int const & p(int const & input) { return input; }
The standard would guarantee it.
How can I pass a given std::vector<float> to a function to match the parameter type float (*parameter)[3]?
The function fills the parameter with coordinates of 3d points, that's the reason for the parameter type to be an array of 3-elements-long arrays. The function is provided by a library and I cannot change it.
I already initialized vector to cover enough elements.
void f(float (*parameter)[3])
{
}
int main()
{
vector<float> v(3);
f(reinterpret_cast<float(*)[3]>(&v[0]));
}
Because there is no way to ascertain that an std::allocator<T> involves in some configuration a float[3], it is not possible to point to such an array given an std::vector<T>. It is not a matter of finding the right cast, or the right value to cast.
The matter is that something like the function you described can only ever be passed a pointer to an actual float[3], or a null pointer.
The only strictly conformant thing you can do is to copy the data to and from a bona-fide float[3] variable, passing a pointer to it to your function.
///////////////////////////////////////
class A {
...
const double funA(void)
{...}
};
A a;
double x = a.funA();
// although the intention is to
// enforce the return value to be const and cannot be
// modified, it has little effect in the real world.
class A2 {
...
double funB(void)
{...}
};
///////////////////////////////////////
class A {
void setA(const double d)
{ // now you cannot change the value of d, so what?
// From my point of view, it is NOT a good practice to change the pass-in parameter
// in this case, unless you want the caller to receive that change
// instead, you can do
// const double dPassIn = d;
/ /then use dPassIn instead.
...
}
};
class A2 {
void setB(double d)
{...}
};
//////////////////////////////////////
From my understanding, we should prefer to
using A2::funB and A2::setB because the const used in
both A::funA and A::setA has little meaning.
// Update //
FMOD_RESULT F_API EventSystem::getReverbPresetByIndex(const int index,
FMOD_REVERB_PROPERTIES *props, char **name = 0);
I consider FMOD is a well-designed package and it does use const int inside function parameter list.
Now, I agree that the A::setA(const double d) has its edge.
When returning by value the constant has no effect as it cannot be enforced anyway. Some compilers issue a warning. However it DOES make sense to return a pointer/reference to constant data.
When passing an argument into a function it is preferable (safer, allows for compiler optimizations) to pass it as a constant unless you absolutely need to change the data.
the const-keyword tells the Compiler "In my function setB i Wont change the Argument. If you want to Optimize for Multithreading you can use this Variable the same Time in another context, because my Work wont change it."
So i would say, in Progamming-logic, the second variant is better, like you said it has "little meaning", but in wider logic if you see what really happens, you should declare const, what is const, and dont declare const what isnt const. It is kind of a documentation the compiler understands and maybe will use to optimize your code!
From my point of view, it is NOT a good practice to change the pass-in parameter
Then it makes sense to declare that by using the const on the argument in the definition of the function. Not everybody follows the practice, so having the const on the argument is better for future readers of your code than having to scan the whole function body for modifications to the argument.
And even if you follow the practice it's easy to modify a variable by mistake (the classic typo of = instead of == or passing the arg via non-const ref or pointer). The const argument in the implementation prevents this.
On the other hand const int argument in the declaration (if separate from definition) does not make sense.
In practice, there is no real benefits for scalars.
However, in theory it could help a compiler to perform additional optimizations, such as passing a reference to the constant instead of copying the double value.
I have a lot of functions with this signature:
DoSomething(int x, int y, int z, int t, int u, int p);
They all have the same number of parameters and the same type of parameters.
I want to be able to use them like this:
DoSomething(1, 2, 3, 4, 5, 6);
I know the compiler cannot distinguish between functions of the same signature (they are plain illegal).
To that affect I would like to wrap the parameters of the functions in logical "Constructs". This does not mean classes or structures. For example:
DoSomething(Construct1(x, y, z), Construct2(t, u, p));
or
DoSomething(Construct1(x, y), Constrcut2(t, u, p, o));
In this case I can distinguish between the two functions and they have the same number of parameters. If I use objects with different constructors even if it is const by ref, using a class or a structure, the Construct is still created. For Example:
DoSomething(const Construct1& constr1, const Construct2& constr2)
{
constr1.x + constr2.t
}
DoSomething(Construct1(1, 2, 3), Construct2(4, 5, 6));
In this case Construct1 and Construct2 are both created.
What I want is:
DoSomething(Construct1(x, y, z), Construct2(t, u, p));
or
DoSomething(Construct1(x, y), Constrcut2(t, u, p, o));
at compile time to expand to:
DoSomething(int x, int y, int z, int t, int u, int p);
thus eliminating the need for the object creation. I am not looking for an object solution. Anything that can expand this is welcome. Even if it is a macro. I am not looking for a complete solution, but if you can point me to what I should read in order to make this myself then that is more than welcome.
Thanks in advance.
Overloading is based on the parameter types rather than the parameter names. You cannot have overloaded functions which have parameter lists with identical types.
I think you have a couple of misconceptions. The most obvious is that the names of the parameters matter... they don't, as far as the compiler is concerned those two function declarations declare a single function that takes 6 integers (consider, if they were different, what would DoSomething( 1, 2, 3, 4, 5, 6 ) do?)
The second misconception is that an object creation necessarily means an allocation. In the code you presented: DoSomething( Object1(x,y,z), Object2(t,y,u) ) there are two objects but not a single dynamic allocation (unless you do them inside Object1 or Object2 constructors).
Overall you should write code that is readable, and only if that proves to be slow, then profile and try to optimize the bottlenecks.
I'm afraid you'll have to find some other route. The compiler ignores any names you give to parameters in a function declaration, so as far as it cares, what you have is:
DoSomething(int, int, int, int, int, int);
DoSomething(int, int, int, int, int, int);
Since there's no difference between these, you aren't declaring two overloaded functions at all -- you're just declaring the same function twice. Attempting to define two functions with that identical signature then violates the one-definition rule.
Edit: Oh, I suppose I should add that without a return type, those aren't allowable function declarations either (not that it's related to the question at hand, but just in case somebody decides to get pedantic about it -- though I can hardly imagine a C++ programmer doing anything like that).
You can avoid unneeded copies by having Object1 and Object2 expose the underlying storage ints
struct Object1 {
...
int x;
int y;
...
}
//<--- passed by reference, no copy happens --->
DoSomething( const Object1& o1, const Object2& o2 )
{
int somethingUseful = o1.x * o2.w - o1.y * o2.z;
}
Please elaborate if you still feel there are copies happening with this approach that are not really needed.
You cannot even define two identical functions in C++, so asking how the compiler can differentiate between the two if hypothetically they did exist is pointless.
Then you seem to be asking how to pass objects in without requiring additional allocations. That is done with const references, but even worrying about this seems premature given that you haven't even got your program's structure solidified yet. Write your program first, then and only then optimise if it is needed. Premature optimisation is the root of all evil.
In some comp-sci papers and tests, I see swap() implemented like so:
void swap(int x, int y, int *a)
{
int t = a[x];
a[x] = a[y];
a[y] = t;
}
Why not simply implement it like so:
void swap(int& x, int& y)
{
int t = x;
x = y;
y = t;
}
Is the idea to the former to make the calling code cleaner, by not having to index into the array for the first two arguments? I realize it's not a very important question, since we should be using std::swap(), but I'm still curious.
Not all programming languages support calling by reference. For instance, the latter way of doing swap doesn't apply to Java.
In books containing pseudo-code, usually there's a convention that arrays and pointers are not copied when passed and everything else is copied in a function call. The former way requires no special explanation about the way arguments are passed.
Regarding your final point about cleanliness, it's not so much different: in the former case, your call to swap would be simply: swap(i, j, a); whereas in the latter, you'll have to swap(a[i], a[j]);, introducing some brackets in the expression.
Your second code sample is C++, NOT C. C++ supports reference parameters, but C only supports reference indirectly via pointers.
I agree that the second implementation is cleaner. In order to make it work in C, change the & in each of the parameters to * and dereference each x and y (also with *) inside the function.