keyboard, computer, keys

Are C++ Objects Passed By Reference or Value?

Lately, there have been questions on whether C++ passes the actual object to a function or a copy of it. In other words, is C++ a pass-by-reference or pass-by-value language?

Consider the following:

				
					string MyString = "Answer = 42";
				
			

What happens when you call a function with our new string?

				
					CallFunc(MyString);
				
			

Does C++ pass in our actual object, or does C++ pass in a copy of our object? In other words, is C++ a pass-by-reference or pass-by-value language?

It’s important to know how we treat parameters to functions and their influence outside of the function if they are altered.

What happens if CallFunc() changes the value of MyString?  Is it reflected upon return from the function?

Testing Function Calls With Strings

				
					void AlterString(string S)
{
    printf("The String is %s\n", S.c_str());
    S = "Thanks for all the fish!";
    printf("The String is now %s\n", S.c_str());
}

string String2 = "42!";

AlterString(String2);
printf("String2 = %s\n", String2.c_str());
				
			
				
					The String is 42!
The String is now Thanks for all the fish!
String2 = 42!
				
			

Some interesting things happened during my test.

First, within the function AlterString(), I was able to change the local copy of the string S.  I printed the value of the string before and after altering it to show it’s possible.  Parameters to a function are treated as variables in a function.

Second, once the function returned and I printed out the value of String2 – you see that the function call only altered its own local copy of the string, not the original String2.

What does this tell us?

C++ is a pass-by-value language.  Each time a function is called, any parameters you pass into the function are copied to the stack and a local variable within the function is assigned to it.  In this case, the function is operating on a copy of String2, placed on the stack, called S.  No matter how much your function alters its own local variables, there will be no effect outside of that function.  As soon as the function returns, everything that was placed on the stack by your function is removed and any local variables are lost.

Consider the following code:

				
					void AlterNumber(int Z)
{
    printf("The Value of Z is %d\n", Z);
    Z = 99;
    printf("The Value of Z is %d\n", Z);
}

int X = 42;
AlterNumber(X);
printf("The value of X after the function call is %d\n", X);

				
			
				
					The Value of Z is 42
The Value of Z is 99
The value of X after the function call is 42

				
			

We can see quite clearly that the function AlterNumber has no effect on the value of X outside its own function definition.  You can change the value of the local variable Z within the function, but it’s only affecting the function’s copy, not the original X.

Pass By Reference – How To:

A function can tell C++ it wants the actual object itself, not a copy of it by using the “&” symbol in the parameter definition.  For example:

				
					void AlterNumberByRef(int& Z)
{
    printf("The Value of Z is %d\n", Z);
    Z = 99;
    printf("The Value of Z is %d\n", Z);
}

int X = 42;
AlterNumberByRef(X);
printf("The value of X after the function call is %d\n", X);
				
			
				
					The Value of Z is 42
The Value of Z is 99
The value of X after the function call is 99
				
			
From the output screen, we see X was successfully altered in our AlterNumberByRef function. Notice the following line contains the “&” symbol:
				
					void AlterNumberByRef(int& Z);
				
			

This informs the compiler that Z is a reference to an existing integer. In this case, Z refers to X. Any changes to Z will affect X since we’re not working with a copy but the actual object.

C++ uses the “&” symbol in a parameter list to denote pass-by-reference.

Objects Are Copied To The Stack

Here’s an example demonstrating how a global string object is copied to a local variable within a function, complete with an address comparison. First, I create a global string object, then pass in that string as a parameter to a function. Since the function has access to the global and local versions, making a comparison is possible.

				
					string String1 = "This Is A String";

void CompareStrings(string Str)
{
    printf("Compare Strings \"%s\" and \"%s\"\n", Str.c_str(), String1.c_str());
    printf("Address of     Str:%lu\nAddress of String1:%lu\n", &Str, &String1);
    printf("  Compare Strings:  Str == String1=%d\n", Str == String1);
    printf("Compare Addresses: &Str == &String1=%d\n", &Str == &String1);
    printf("   Compare Length:  Str, String1=%d, %d\n", Str.length(), String1.length());
    printf("   Compare Sizeof:  Str, String1=%d, %d\n", sizeof(Str), sizeof(String1));
    Str = "Different String";

}


void CompareStringsByRef(string& Str)
{
    printf("Compare Strings \"%s\" and \"%s\"\n", Str.c_str(), String1.c_str());
    printf("Address of     Str:%lu\nAddress of String1:%lu\n", &Str, &String1);
    printf("  Compare Strings:  Str == String1=%d\n", Str == String1);
    printf("Compare Addresses: &Str == &String1=%d\n", &Str == &String1);
    printf("   Compare Length:  Str, String1=%d, %d\n", Str.length(), String1.length());
    printf("   Compare Sizeof:  Str, String1=%d, %d\n", sizeof(Str), sizeof(String1));
    Str = "Different String";
}


printf("Testing Pass by Value vs Pass By Reference\n");

printf("Pass By Value:\n");
CompareStrings(String1);
printf("String1 is now : %s\n", String1.c_str());

printf("---------------------------------\n");

printf("Pass By Reference:\n");
CompareStringsByRef(String1);
printf("String1 is now : %s\n", String1.c_str());
				
			
				
					Testing Pass by Value vs Pass By Reference
Pass By Value:
Compare Strings "This Is A String" and "This Is A String"
Address of     Str:12449540
Address of String1:3217252
  Compare Strings:  Str == String1=1
Compare Addresses: &Str == &String1=0
   Compare Length:  Str, String1=16, 16
   Compare Sizeof:  Str, String1=28, 28
String1 is now : This Is A String
---------------------------------
Pass By Reference:
Compare Strings "This Is A String" and "This Is A String"
Address of     Str:3217252
Address of String1:3217252
  Compare Strings:  Str == String1=1
Compare Addresses: &Str == &String1=1
   Compare Length:  Str, String1=16, 16
   Compare Sizeof:  Str, String1=28, 28
String1 is now : Different String
				
			

It’s pretty clear from the demonstration that C++ passes objects to a function by value unless you specify pass-by-reference using the “&” symbol.  Using the 2nd function, CompareStringsByRef produces a reference to the passed-in string object.

So in this regard, C++ treats objects the same as any built-in type when calling a function.  Even our own classes:

				
					class TestClass
{
    public:
        int X = 4;
        int Y = 2;

        int XxY()
        {
            return X * Y;
        }

};


void ChangeTestClass(TestClass TC)
{
    TC.X = 22;
    printf("In Function: XxY=%d\n", TC.XxY());
}


void ChangeTestClassWithReference(TestClass& TC)
{
    TC.X = 22;
    printf("In Function: XxY=%d\n", TC.XxY());
}

TestClass TC;
printf("Out of Function: XxY=%d\nPass By Value:\n", TC.XxY());
ChangeTestClass(TC);
printf("Out of Function: XxY=%d\nPass By Reference:\n", TC.XxY());
ChangeTestClassWithReference(TC);
printf("Out of Function: XxY=%d\n", TC.XxY());

				
			
				
					Out of Function: XxY=8
Pass By Value:
In Function: XxY=44
Out of Function: XxY=8
Pass By Reference:
In Function: XxY=44
Out of Function: XxY=44
				
			

Our TC object was altered successfully when passed by reference but not when passed by value. This is normal behaviour for intrinsic types such as int, float, bool etc. But it’s also normal for objects.

Conclusion

In C++, passing objects around is the same as passing built-in types around. Thus, they behave the same and require space on the stack when passed to a function to create a local, distinct copy.

Since strings and other objects can require a lot of memory, we don’t usually pass strings by value, instead opting for a reference (or, even more likely, a pointer).  Passing by value creates a new copy of the object and places it on the stack, quickly gobbling up memory.

We have mechanisms for altering an actual object rather than a copy of it. For instance, a function can request a reference to an object rather than its own distinct copy.

Although I didn’t discuss pointers in this article, they are similar to references in that they provide access to the memory occupied by an object.  Pointers and references are the usual way to alter external variables.

But at the end of the day, I’ve shown how objects are treated like built-in types when passed into a function. For example, space is allocated on the stack and freed when the function returns.