Code Comments
Programming Forum and web based access to our favorite programming groups.Hi NG
A few days ago there was a thread in comp.lang.c++.moderated with the
subject "Return versus Side-Effect". The OP asked whether to use "return by
value" or an additional parameter of type "C&" which will be overwritten
with a result.
The most posters advised the OP to use the version with the return value. No
only for aesthetic reasons but also for performance reasons because of the
return value optimization.
I wrote a little sample program to test their assertion.
For aesthetic reasons I prefer the "return by value" version. But I found
out that for performance reasons both versions are necessary. It depends on
how you call the function. Sometimes the "return by value" is faster than
the "out-parameter" version and sometimes the other way around. Can you
confirm my conclusion?
Best regards,
Chris
Here is my test code:
#include <iostream>
using std::cout;
#include <iostream>
using std::cout;
class MyString
{
public:
MyString() : value(NULL) { cout << "ctor 0\n"; }
MyString(const char*);
MyString(const MyString&);
~MyString() { delete[] value; }
MyString& operator=(const char* newValue);
MyString& operator=(const MyString&);
private:
char* value;
};
MyString::MyString(const char* newValue)
{
cout << "ctor 1\n";
if (newValue == NULL) {
value = NULL;
} else {
int length = strlen(newValue);
value = new char[length + 1];
strcpy(value, newValue);
}
}
MyString::MyString(const MyString& other)
{
cout << "copy ctor\n";
if (other.value == NULL) {
value = NULL;
} else {
int length = strlen(other.value);
value = new char[length + 1];
strcpy(value, other.value);
}
}
MyString& MyString::operator=(const char* newValue)
{
cout << "op =0\n";
if (value != NULL) {
delete[] value;
} // if
if (newValue == NULL) {
value = NULL;
} else {
int length = strlen(newValue);
value = new char[length + 1];
strcpy(value, newValue);
}
return *this;
}
MyString& MyString::operator=(const MyString& other)
{
cout << "op =1\n";
if (this == &other) {
return *this;
} // if
if (value != NULL) {
delete[] value;
} // if
if (other.value == NULL) {
value = NULL;
} else {
int length = strlen(other.value);
value = new char[length + 1];
strcpy(value, other.value);
}
return *this;
}
MyString returnByVal()
{
MyString ret("return value");
return ret;
}
void returnByParam(MyString& out)
{
out = "return value";
}
int main()
{
// First test case where the out parameter is faster.
cout << "Before returnByVal()\n";
MyString str1;
str1 = returnByVal();
cout << "\nBefore returnByParam()\n";
MyString str2;
returnByParam(str2);
cout << "Finished test 1\n\n\n";
// Second test case where return by value is faster.
cout << "Before returnByVal()\n";
MyString str3 = returnByVal();
cout << "\nBefore returnByParam()\n";
MyString str4;
returnByParam(str4);
cout << "Finished test 2\n";
}
The output is the following:
Before returnByVal()
ctor 0
ctor 1
op =1
Before returnByParam()
ctor 0
op =0
Finished test 1
Before returnByVal()
ctor 1
Before returnByParam()
ctor 0
op =0
Finished test 2
In the first test case the "out-parameter" version is faster.
In the second test case the "return by value" version is faster.
Post Follow-up to this messageOn Apr 3, 10:15 am, "Christian Meier" <chris@no_spam.com> wrote:
> Hi NG
>
> A few days ago there was a thread in comp.lang.c++.moderated with the
> subject "Return versus Side-Effect". The OP asked whether to use "return b
y
> value" or an additional parameter of type "C&" which will be overwritten
> with a result.
> The most posters advised the OP to use the version with the return value.
No
> only for aesthetic reasons but also for performance reasons because of the
> return value optimization.
> I wrote a little sample program to test their assertion.
> For aesthetic reasons I prefer the "return by value" version. But I found
> out that for performance reasons both versions are necessary. It depends o
n
> how you call the function. Sometimes the "return by value" is faster than
> the "out-parameter" version and sometimes the other way around. Can you
> confirm my conclusion?
> [...]
Change this:
>
> MyString& MyString::operator=(const MyString& other)
> {
> cout << "op =1\n";
> if (this == &other) {
> return *this;
> } // if
>
> if (value != NULL) {
> delete[] value;
> } // if
>
> if (other.value == NULL) {
> value = NULL;
> } else {
> int length = strlen(other.value);
> value = new char[length + 1];
> strcpy(value, other.value);
> }
> return *this;
> }
Into:
MyString& MyString::operator=(MyString other)
{
value = other.value;
other.value = 0;
cout << "op =1\n";
return *this;
}
the output doesn't change, but now the "op=1" operation is quite
inexpensive.
--
gpd
Post Follow-up to this messageIn article <6a853$47f49214$3e024b42$18038@news.hispeed.ch>,
Christian Meier <chris@no_spam.com> wrote:
>
Deleted String class too complex to best illustrate NVRO.
All you need is a very simply class the cout
Constuctor/Copyconstructor/operator=. All the rest obfuscate.
>
>int main()
>{
> // First test case where the out parameter is faster.
> cout << "Before returnByVal()\n";
> MyString str1;
You create the object using the default constructor for no reason.
If you tell the compiler that it must create a default initialised
object then that is what it does even if a human code reviewer would
question why you felt the need to default initialise it and
immediately assign to it. The compiler can't send you an email
asking: "Are you sure that's really what you want to do?"
> str1 = returnByVal();
then assign to it
> cout << "\nBefore returnByParam()\n";
> MyString str2;
> returnByParam(str2);
> cout << "Finished test 1\n\n\n";
>
> // Second test case where return by value is faster.
> cout << "Before returnByVal()\n";
> MyString str3 = returnByVal();
Here you create and initialise correctly all at once. That's better
> cout << "\nBefore returnByParam()\n";
> MyString str4;
> returnByParam(str4);
> cout << "Finished test 2\n";
>}
>
>The output is the following:
>
>Before returnByVal()
>ctor 0
>ctor 1
>op =1
>
>Before returnByParam()
>ctor 0
>op =0
>Finished test 1
>
>
>Before returnByVal()
>ctor 1
And your test shows that there are less methods called.
>Before returnByParam()
>ctor 0
>op =0
>Finished test 2
>
>
>In the first test case the "out-parameter" version is faster.
>In the second test case the "return by value" version is faster.
Hmm, maybe. You have only traced number of methods called. Not speed
so you are assuming that this is faster. It might not be and it might
not be relevant.
Yan
Post Follow-up to this messagePowered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.