[C++]Quick question about passing by value

camelCase

The Case of the Mysterious Camel.
Reaction score
362
Say, I have this Utility class that only has one member variable.
Code:
class SomeHelper {
    public:
        //Constructor, methods, etc.
    private:
        float someFloat;
};

Would it be more efficient to pass it around by value or by reference if I don't need to modify the value of SomeHelper?

IIRC, it's better to pass by value if it contains only one member variable.. But I can't be sure. But I feel awkward passing every other class instance by reference and only passing this by value. It feels.. Wrong. Like, stylistically inconsistent.

I duno, assuming I care about the minute performance difference rather than style consistency, would passing by value or reference be better?
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
How would you pass an object "by value"? I thought this discussion does only make sense for primitive data types since all objects are always passed by reference.
Its not like anybody would create an exact copy of the object in question and pass that.
 

camelCase

The Case of the Mysterious Camel.
Reaction score
362
In C++, everything is passed by value by default. Well, the object being passed by value is copy-constructed.
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
Thats a horrible idea. There arenot many situations (none) when this could be of any use. And obviously copying all data can never be beneficial to performance.
 

camelCase

The Case of the Mysterious Camel.
Reaction score
362
It's not so bad. Default copy-constructors do a bit-wise copy, meaning it's just blasting through memory and is fast.

However, there are pointers and whatnot to take into account and that's when you need user-defined copy-constructors for a "deep copy".

Then, there are move-constructors (recent addition) that's like a copy-constructor but more efficient, if done right, for deep copying (or, rather, moving).

My question, I guess, is more like this:
Is it better to blast through memory for four-bytes and have that on the stack throughout its use in the function (pass by value) or keep dereferencing (IIRC, references, when compiled to assembly, are auto-dereferencing pointers [even though it's wrong to think of it that way]) each time I access the variable (pass by reference)?

I'm leaning to passing by value in this case because you're only copying four-bytes once and subsequent access within the function requires no dereferencing. Passing by reference has no weird copying going on but you've got this auto-dereferencing with each access which would be slower.

But I'm no expert on the internals of C++ and might be wrong =P

Also, if I pass by value and only access it once.. And pass by reference and access only once.. Which would, then, be better? (Even though the difference would be negligible, I am curious..)
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
Copying is still very costly. You need to find an empty space in the RAM and you need to free it later. (maybe even garbage collect, I dont know)
And then there is the overhead. The function call to the copy constructor, creating a new reference to the copy, etc etc.
References are lightning fast, with our CPU's of today its by far better to do minuscule work then to copy within the painfully slow RAM.
 

camelCase

The Case of the Mysterious Camel.
Reaction score
362
Passing by value would cause the class instance to be allocated on the memory stack, so it's quick. But you might be right about the CPU vs RAM thing but references still rely on RAM access, no?

But I imagine this is what happens when passing by reference:
01) Call function, pass by ref
02) Address is copied, stored in function-local var
03) To access object, address in local var is read
04) Then, object is found in address read earlier on

When passing by value:
01) Call to function, pass by value
02) Copy constructor invoked
03) value passed is bit-wise copied to local var
04) To access, it's already there

I honestly don't know anything about computer hardware =x

Edit:
Actually, I don't know why I got so curious about it. I'm just gonna' stick to passing by const-ref instead of passing by value.
 

s3rius

Linux is only free if your time is worthless.
Reaction score
130
Ok, let's start clearing up some things :)
( I love this stuff :p )


First of all: yes, pass by value will be better. Why?

The alternative is a reference.

First, what exactly is a reference? It really isn't any different from a const pointer, with the exception that it has to be initialized to an object.
Just like a pointer, a reference will need a few bytes of space to be passed (basically you're passing the reference by value). On a 32bit system that will likely be 4 byte, on a 64bit system it'll be 8 byte.

So no matter what you do, you will always have to copy a few bytes around. Since a SomeHelper object is only 4 byte big (it only contains a float, after all) you'll fare better just using a pass-by-copy if you're looking at how many bytes have to be copied.

Second, compilers also optimize code. Usually the generated code would copy the SomeHelper object onto the stack, then call the function, then read the object from the stack again. But a common optimization is to move the object into a register rather than the stack. Registers are faster, no RAM access has to be performed. I don't know of any compiler which doesn't do this for small objects. (btw, compilers can even do that for objects that are too large to fit into a register, they'll split the object into "parts" and use several registers.)

Third, there are other kinds of performance optimizations that a compiler cannot necessarily do when you are passing by reference.
I tried to construct an example, but I couldn't think of something that made sense. Just think: two objects passed by value are always distinct objects. Two objects passed by reference could refer to the same object. That can cause problems for the compiler.

Four, as you've said, you'll need a dereference operation for a reference. So that's only extra weight added to it (if the compiler can't get rid of it).
This dereference operation can actually cause more problems. Objects on the stack are always near each other. It's very easy for the CPU to keep the needed regions of stack memory cached in L1/L2 cache. But references (like pointers) can refer to anywhere. During dereferencing, the referenced memory region must be cached aswell. So if you grab references from all over your memory, you'll end up having to keep tons of memory regions inside your cache just for the sake of a single reference. But I'm not sure if that would be bigger problem. Maybe on mobile devices where you have little RAM available. On a desktop you probably don't have to care much unless you fk up royally.


Thats a horrible idea. There arenot many situations (none) when this could be of any use. And obviously copying all data can never be beneficial to performance.

Actually there are good reasons to do so. The biggest one is cache locality. Just because something is in memory doesn't mean it has to be read/written from there. The CPU constantly moves parts of memory in and out of L caches. And L caches are lightning fast. That's why keeping data close together is such a good idea (e.g.: vector = fast, linked list = slow, because vector keeps all it's data together).
Compilers are very good in optimizing for copy cases too. There is copy elision to get rid of temporary objects, there is move construction to get rid of rvalues, as mentioned above there are registers which can do the work for you.

Working on the stack is also relatively faster.
Look at Java, on the other hand. In theory, Java has only heap allocation for objects. That's what C++ programmers call a performance nightmare. And yet, Java isn't all that slow, because it actually falls back to stack-copying where-ever it can, which is great for cache locality again.

Copying is still very costly. You need to find an empty space in the RAM and you need to free it later. (maybe even garbage collect, I dont know)
That's the beauty of C++. There is no garbage collection, so there is no overhead of this sort.
And the stack is much easier to work with:

342px-Call_stack_layout.svg.png


This is a visual representation of the stack. The blue and green colored parts each belong to a function call - DrawSquare (blue) and DrawLine (green) in this case.
Imagine DrawLine calls another function. This new function would put all it's data right ontop of the green pile (ontop of the stack). Once the function is finished, it'll just chop off the entire chunk of stack that belonged to it and return back to DrawLine.
New objects or function calls are always put on top of the stack, so there is no need to look for a free space in RAM. There is no need to free them (=> mark the space as not-used) because you just chop off the entire chunk that belonged to the function at once.

What you were thinking of is heap allocation, which is indeed more costly.


Also, if I pass by value and only access it once.. And pass by reference and access only once.. Which would, then, be better? (Even though the difference would be negligible, I am curious..)

Pass-by-value for simple objects, basically PODs.

Pass by const-reference for anything that has copy constructers that need to do extra work like having to heap allocate, or if an object contains resources that shouldn't be copied (e.g. a std::fstream, or a std::vector).

There's a nice article called "Want speed? Pass by value." but the website is currently down.
I hope it'll come back up again, it was a good read.
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


The good moral of the story: Cache locality is the winner. Avoiding heap allocation takes second place.



PS:
In C++, everything is passed by value by default. Well, the object being passed by value is copy-constructed.

Aaaaactually not quite true, and then again very true :D
It's not quite true because arrays decay to pointers when being passed. These two functions are the same:
Code:
void func( char* a ) { ... }
void func(char[] a ) { ... }

It's an old relic from C. Back in the day people were concerned with the performance implications of passing large arrays of data to functions. So they decided that arrays would be treated special. Instead of passing by value, they would always only pass a pointer to the array.

But on the other hand it's very true since even pointers or references are passed by value. They will still be bitwise copied over to the callee when calling a function.

PPS:
Heh, yesterday I got my trophy for 5-year member :D
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
Compiler optimization. Didnt think of that. Makes sense what you say here.

But I still dont see a good reason to copy your object instead of using the same object. I mean, thats the point of object oriented programming, isnt it?
I you just use your objects like primitive data types then it kinda loses its purpose.
 

s3rius

Linux is only free if your time is worthless.
Reaction score
130
Object-oriented programming is the concept of working with objects. There's no statement about how to treat the objects.

From a performance point of view I've detailed why pass-by-value and pass-by-ref both have their merits in different situations.

So what is left is pretty much only how it affects working with the language.

And at that point it's pretty much down to personal preference.
I love the way C++ handles objects and object lifetime. It's clear, deterministic and practically useful.

I can't really argue here, because I don't exactly know what you mean. In what way do you think does it defeat the purpose of oop?
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
In my opinion the reason for object oriented programming is to handle data "like we would do in the real world". We could have everything in gigantic tables, arrays, likea database system, but we try to create objects which hold certain data about themselfs like objects in real life.
Lets say we have a class "car" with various attributes and we have a class "carDealer" with the method "public Deal makeDeal(Car someCar)".
if you pass by value then you would make a copy of your car and give that copy to the dealer who would make you a deal and destroy the card later on.
That just sounds wrong to me. Thats not how its supposed to be. You dont just copy objects around and throw them away left and right because thats not how its supposed to be.

In my opinion the goal is to simulate real life events as close as possible / reasonable.

Of course, I guess you are right about the performance, but that just sounds wrong to me.


By the way, can you pass enum's by value in C++?
 

camelCase

The Case of the Mysterious Camel.
Reaction score
362
Agh, using the forums from my phone sucks. Enums are, internally, ints in C++.
This means that, yes, you pass enums by value. Enums in Java are object instances and comparing them is comparing their object references or w/e, iirc. In C++, enums are primitive integral types.

This makes binary compatibility for C++ a headache where enums are concerned, though. I think C# has that problem, too. Not sure about Java.

Thanks, s3rius! I'll read it once more through when I get home!

I kinda agree with accname but we're talking negligible performance boost in my case.. And optimization is a crazy world because, in assembly, all our constructs like classes and enums have no meaning. They're things someone cooked up in higher level languages to make life easier.

Also, I knew a guy who refused to use the 1st element in arrays, starting arrays from index 1 instead, because he said using "i-1" all over the place was slow, lol.

Lord knows I was not made for the world of micro optimizations
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
Yes, enums are ints internally in C++ but can you compare them to ints? Can you do arithmetics with enums?
By design enums should be unique and from a finite set. If you pass by value, that is, make a copy of an enum, doesn that violate the design?

In java an enum is an object from a special class which has a private constructor and public static final instance variables, and the additional constraint that the constructor can never be called during runtime.
 

camelCase

The Case of the Mysterious Camel.
Reaction score
362
No, you cannot do arithmetic with enums unless you cast them to ints. Yes, you can compare them to ints because enums are implicitly casted to ints.

Code:
enum TestEnum {
    A, B, C
};
int main () {
    TestEnum e = TestEnum::A;
    if (e == 0) {
        //Works because e is implicitly cast to an int before comparison
    }
    if ((int)e == 0) {
        //Same as above, but explicitly cast
    }
 
    e = e + 30; //e+30 works, but the result is an int and the assignment fails
    e = (TestEnum)(e+30); //e+30 works, cast works, e now has the value of (e+30)
    //e+30 is.. Definitely not going to be A, B or C, therefore, yes, you HAVE, indeed, managed
    //to cause enums in C++ to break. But, as any sane coder will say, if you're taking the pains
    //to write bad code, you deserve to shoot yourself in the foot
 
    //The only valid reasons for using casting an int to an enum are:
    //+ You know it will always be in range
    //+ You are deserializing a network packet
    //+ You are mapping enum elements to an array
    //+ Well, I only know those three reasons =P
    return 0;
}

The thing about C++ is that it allows you to write bad code if you want to.. But it will almost always be obvious that you're trying something funny. Look at const_cast! It allows you to explicitly remove the const modifier from a variable. Suddenly, const variables have their values changing, ew. But it's there for integrating libraries that you have no control over, I suppose.

Then, there's also this thing, I think it's called class shadowing or something; 99.9999% sure it's the wrong term.
Code:
class YouHaveNoControl {
    private:
        int m_CantReachMe;
};
class Shadow {
    public:
        int m_PUBLICOMGWTFBBQ;
};
 
YouHaveNoControl a;
Shadow& b = reinterpret_cast<Shadow&>(a);
b.m_PUBLICOMGWTFBBQ = 9001; //At this point, a.m_CantReachMe is 9001!

It's obviously a horrible idea to do this in 99.999% of cases but this little trick served me well in one of the AI coding modules I took last sem. My lecturer gave us all some code-base to work off and we didn't have access to the source files.. And there were a lot of agent stats that were not accessible because he forgot to have public getters for them.

So, I used that little reinterpret_cast trick to read the variables I needed.

[Edit]
Bookmarking this for the amount of useful info. (Y)
 

s3rius

Linux is only free if your time is worthless.
Reaction score
130
In my opinion the goal is to simulate real life events as close as possible / reasonable.

Wouldn't it be called Real World Oriented programming then? :3

Wikipedia said:
Objects can be thought of as encapsulating their data within a set of functions designed to ensure that the data are used appropriately, and to assist in that use.

Objects often are related to how things would work in the real world, because of the above statement. If you're constructing an object and give it the ability work with then you might as well call it a Car Dealer.

A class is supposed to group related functionality together. It often co-related with the real world because we naturally group related functionality in reality too.

A Car Dealer isn't going to sell you ice cream, just like an array won't print messages to a console.

Even in Java you have many classes that don't represent objects in real life.
Look at interfaces like Comparable<T>. It's implemented by classes that you can compare with each other. It's a concept (or a property), rather than an object.

You could say C++ goes a less ... real-world oriented way. But it's OOP nonetheless. As a programmer one has to learn that the real world isn't always a good example to model your programs after. Just like other programming paradigmas like Data Oriented Design or Functional Programming, Object Oriented Programming has more to it than things that could be called objects in real life.

But it also seems like you have a wrong image of C++. You can basically code up simple applications without much worrying over object destruction and copying.

Code:
class Person{
public:
    string Name;
    int Age;
}
 
bool compareTwoPersons( Person first, Person second ){
    return first == second;
}
 
void main(){
    Person a;
    a.Name = "John";
    a.Age = 25;
 
    Person b;
    a.Name = "Lisa";
    a.Age = 22;
 
    if( compareTwoPersons( a, b ) ){
        cout << "Oh God, what sorcery!";
    }
    if( compareTwoPersons( a, a ) ){
        cout << "Yes, as it's supposed to be.";
    }
}
This code is totally OK. It could be more efficient, sure. But it will work just fine. With the exception of a few minor things this could be Java code. You don't even notice that the function call to compareTwoPersons() actually creates temporaries that will be discarded soon after.
(And I'd venture to say that the optimizer throws out basically all unneeded copying once you compile it.)

Yes, enums are ints internally in C++ but can you compare them to ints? Can you do arithmetics with enums?

By design enums should be unique and from a finite set. If you pass by value, that is, make a copy of an enum, doesn that violate the design?

Being able to instantiate it doesn't violate the design. What I can do is:

Code:
EnumType a = EnumType::SomeEnumValue;
EnumType b = EnumType::SomeEnumValue;

Now I have two instances of EnumType with the same value. a == b. However, they're two distinct objects. &a != &b.
It's like buying tickets for Disney Land. Every customer gets his own ticket, but all tickets are the same.

Doing arithmetic on enums is a relic from C. Back then enums were added hastily and weren't all that well thought out, so they just made them act like ints.
But people use enum arithmetic for one nice thing: it allows you to combine enums.

Let's say I have an enum that I use to create program windows with different properties, so-called bit flags.
Code:
enum WindowState{
    DOUBLE_BUFFERED = 0x1,
    FULLSCREEN = 0x2,
    ALWAYS_ON_TOP = 0x4
}
 
CreateWindow( DOUBLE_BUFFERED ); //Creates a double buffered window
CreateWindow( DOUBLE_BUFFERED | FULLSCREEN ) //Creates a window that's doub. buff. AND fullscreen.

This works with simple bitwise logic. It's easier to see when we recreate the example with integral data types instead of enums. I'll use unsigned char because it's the smallest data type available.
Code:
unsigned char DOUBLE_BUFFERED = 0x1; // 00000001 in binary
unsigned char    FULLSCREEN = 0x2;    // 00000010 in binary
unsigned char ALWAYS_ON_TOP = 0x4;      // 00000100 in binary
 
//Create a enum value that is double buffered and fullscreen:
unsigned char myEnumValue = DOUBLE_BUFFERED | FULLSCREEN;
//That means it's  00000001  |  00000010 => 00000011
 
If I want to figure out which "bit flags" are set in my enum value:
 
bool isDoubleBuffered = myEnumValue & DOUBLE_BUFFERED; //true
bool isFullscreen = myEnumValue & FULLSCREEN; //true
bool isAlwaysOnTop = myEnumValue & ALWAYS_ON_TOP; //false

This pattern is used a lot. You can see it in Windows OS, OpenGL, Linux (I think), many third-party libraries like SDL, etc.

"Normal" C enums are a strange thing. They don't care if they contain invalid values. In the above example, the program wouldn't care if I set myEnumValue = 23423423; It would just recognize it as not being any of the default enum states.

In C++ there are a new type of enum, called Enum Classes which are more like Java enums in that you can't cast them to an int and you can't do arithmetic with them. They're more typesafe and usually the way to go if you don't want bit flags.

So, I used that little reinterpret_cast trick to read the variables I needed.

You can shadow it even easier by interpreting simply it as an int.

Code:
class YouHaveNoControl {
    private:
        int m_CantReachMe;
};
 
YouHaveNoControl a;
*((int*) &a) = 5; // The "C" way
 
//or:
//    int& b = reinterpret_cast<int&>(a);
//    b = 5;
 
//or even:
// reinterpret_cast<int&>(a) = 5;
// works because it's a reference :)
 
//oh, and this works too:
// ((int&) a) = 5;
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top