• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
slicer4ever

understanding return by value.

9 posts in this topic

hey good people of gamedev, i've been studying and working in c++ for a good number of years, but their has been a key component of c++ that i've simply shyed away from. Because each attempt i made to research it, and understand it. it left me worried in how to approach situations, so in general i avoided it at all costs, in short, i'm talking about retuning a class object which isn't just a POD type.

for example: [url="http://codepad.org/yvEu6DqW"]http://codepad.org/yvEu6DqW[/url]

and just incase you don't like to click away, here's the sample code of the test i made:
[code]
#include <iostream>
using namespace std;
class Test{
public:
int m_ID;
Test Make(int ID){
return Test(ID);
}
Test operator + (Test &o){
return Test(m_ID+o.m_ID);
}
Test(int id) : m_ID(id){
cout << "Created Object " << m_ID << endl;
}
~Test(){
cout << "Destroyed Object " << m_ID << endl;
}
};
int main(int argc, char **argv){
Test A = Test(10);
Test B = A.Make(11);
Test C = A+B;
cout << "Object: "<<A.m_ID<<endl;
cout << "Object: "<<B.m_ID<<endl;
cout << "Object: "<<C.m_ID<<endl;
return 0;
}
[/code]

essentially, from looking at the output, i read it as this:

Test A is created by declaring it, but then when i set it = to Test(10) it destroys the old object, and create's a new one in it's place.

then, i create B, again B being declared causes the constructor to be called, then immediately the destructor is called, after A.Make is even called, so the destroyed object contains the right value, but this extra code is still being called anyway.

I understand that many compiler's might compile out the return values so instead of storing a temp version to copy into the result, it simply copy's the result directly into the return value's location.

still, it's very confusing, and the memory overhead of what's going on is what scares me into only doing this sort of stuff via passing by reference, and using new/delete instead of ever using stack memory to hold the object. Edited by slicer4ever
0

Share this post


Link to post
Share on other sites
If you want all the object creations you also need to look at the copy constructor. Currently you didn't define one, so the compiler did.

The signature looks like this:

Test::Test( const Test& rhs )


2

Share this post


Link to post
Share on other sites
Return by value is just copying the data. If you have an object that is 4 bytes like a int is. And you have a function
[source lang="cpp"]
void SomeFunc(int x)
{
x = x + 1;
}
[/source]
[source lang="cpp"]

[/source]

you call the function
[source lang="cpp"]
int myint = 50;

SomeFunc(myint);
[/source]

what do you think the value of myint is now? It's still 50. When you called Somefunc it copied the 4 bytes into the functions parameter, on the stack. Every function data goes onto the stack as stack frames. When the function returns all that data is freed. Even when you write your own classes, the language knows the size of your objects and will just copy them bit for bit. Every time one is copied there is also a constructor call and when these variables are destroyed there is a destructor call as well. The only way to get around this behavior is using pointers. Even though the same thing is happening to the pointer data itself. If you copy a pointer it still has the same address which is the address of some object.

So if you wanted to make changes to myint inside the function you will have to use pointers or references( references use pointers ), to refer back to the variable you're really wanting to modify.

[source lang="cpp"]

void SomeFunc(int* x)
{
*x = *x + 1;
}
[/source]

In this function body the * is the dereference operator, this is how you access the object the pointer is pointing to.

and call it like
[source lang="cpp"]

int myint = 50;
SomeFunc(&myint);
[/source]

In this snippet we create a pointer by getting the address of myint using the & operator. Since myint is an int this is an int pointer. Which points to the original object.

Your issue is not understanding the stack and heap. Understanding how these two things work in cpp and how they interact is fundamental to this language. Read up on this until you have it, but it really is fundamentally simple. So once it clicks, you'll have it for life. Edited by EddieV223
1

Share this post


Link to post
Share on other sites
If you want to avoid the intermediate constructor/destructor you could explicitly use the copy constructor like so:
[CODE]
Test B( A.Make(11) );
[/CODE]

To avoid the copy during method return you either have to return a reference or a pointer, but for this you'll need to guarantee that the object will survive beyond the method's scope. You could also use reference out parameters like so:
[CODE]
bool Make(int ID, Test& outParam )
{
outParam = Test(ID);
return true; //Return something for the user to know if creation succeeded.
}
int main(int argc, char **argv)
{
Test A = Test(10);
Test B;
A.Make(11, B);
}
[/CODE]
1

Share this post


Link to post
Share on other sites
I think you're overthinking.
If a language was so subtle and inefficient it would not have remaind so dominant for so many decades.

Consider this complete and test example. Build it. Run it.
Observe the sequence in which the constructors and destructors are called.

It's important to understand the lidetime of objects in C++.
You also need to understand the cost of new/new[] and delete/delete[] if you're worried about
unnecessary resource consumption.
[source]#include <iostream>

class Test
{
public:
Test(int id)
: id_(id)
{ std::cerr << " ctor(" << id_ << ")\n"; }

Test(Test const& rhs)
: id_(rhs.id_)
{ std::cerr << " copy-ctor(" << id_ << ")\n"; }

~Test()
{ std::cerr << " dtor(" << id_ << ")\n"; }

Test& operator=(Test const& rhs)
{
std::cerr << " operator=(" << rhs.id_ << ")\n";
id_ = rhs.id_;
return *this;
}

Test& add(Test const& rhs)
{
std::cerr << " (" << id_ << ").add(" << rhs.id_ << ")\n";
id_ += rhs.id_;
return *this;
}

private :
int id_;
};

Test operator+(Test const& lhs, Test const& rhs)
{
return Test(lhs).add(rhs);
}

int
main()
{
std::cerr << "Test A(10);\n";
Test A(10);
std::cerr << "Test B = Test(11)\n";
Test B = Test(11);
std::cerr << "Test C = A + B\n";
Test C = A + B;
std::cerr << "end\n";
}[/source]
Here's the output.
[code]
Test A(10);
ctor(10)
Test B = Test(11)
ctor(11)
Test C = A + B
copy-ctor(10)
(10).add(11)
copy-ctor(21)
dtor(21)
end
dtor(21)
dtor(11)
dtor(10)
[/code]
Notice that there are not extra calls to the basic constructor when using the assignment initialization form of declaration.
You might also notice the extra temporary constructed in the [font=courier new,courier,monospace]operator+()[/font] function.
2

Share this post


Link to post
Share on other sites
[quote name='slicer4ever' timestamp='1346813951' post='4976662']
still, it's very confusing, and the memory overhead of what's going on is what scares me into only doing this sort of stuff via passing by reference, and using new/delete instead of ever using stack memory to hold the object.
[/quote]

Don't be scared. Until you've profiled your code, and determined that it runs too slowly as a direct result of passing by value, always pass by value (where possible.) There are two sides to the argument. Heap allocated memory comes with its own set of difficulties, and they're rather more unpleasant than the problems associated with pass by value.

I've never been more productive since I virtually eliminated heap allocations from my own code: [url="http://purplepwny.com/blog/want_sanity_pass_by_value.html"]http://purplepwny.co...s_by_value.html[/url]
2

Share this post


Link to post
Share on other sites
thanks for the help guys, i'm understanding alot more.

After adding the copy constructor, i can see where alot of my confusion was coming from, I couldn't figure out how the destructor twice had the correct value, when the object had only been created once. now with the copy constructor, I see what's going on better.

I've been messing around alot more with this, and thanks to you guys, I think i have a much better grasp of what's going on.

@Murdocki:
That's essentially what I have been doing for along time, but it's time I start doing things in a much better fashion.

thanks for the help guys=-)
1

Share this post


Link to post
Share on other sites
in the C++ spec, RVO is mentioned to work only with initialization of objects.
[CODE]
A a = MakeMeAnA(); // ok
...
A a;
a = MakeMeAnA(); // not ok.
[/CODE]
be careful.

also, C++11 solved the issue thanks to std::move
0

Share this post


Link to post
Share on other sites
[quote name='Lightness1024' timestamp='1346936187' post='4977168']
in the C++ spec, RVO is mentioned to work only with initialization of objects.
[CODE]
A a = MakeMeAnA(); // ok
...
A a;
a = MakeMeAnA(); // not ok.
[/CODE]
be careful.[/quote]
That is not entirely correct, as I understand it. The second example wouldn't ever invoke a copy-constructor anyway. It would invoke the assignment operator (and side-effects in overloaded assignment operators aren't just ignored).

However, if your class does [b]not[/b] have an explicitly defined assignment operator, GCC [b]will[/b] use RVO to eliminate the copy (i.e. the copy constructor will not be called, even if it has non-trivial side-effects.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0