Explain this code to me please :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
//  main.cpp
//  Critter Farm
//
//  Created by Jacob Jones on 11/02/2013.
//  Copyright (c) 2013 Jacob Jones. All rights reserved.
//

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Critter
{
public:
    Critter(const string& name = ""): m_Name(name) {}
    string GetName() const { return m_Name; }
private:
    string m_Name;
};

class Farm
{
public:
    Farm(int space = 1) { m_Critters.reserve(space); }
    void Add(const Critter& aCritter) { m_Critters.push_back(aCritter); }
    void RollCall() const
    {
        for (vector<Critter>::const_iterator iter = m_Critters.begin();
             iter!= m_Critters.end(); ++iter)
            cout << iter->GetName() << " here.\n";
    }
private:
    vector<Critter> m_Critters;
};

int main(int argc, const char * argv[])
{
    Critter crit("Poochie");
    cout << "My critter's name is " << crit.GetName() << endl;
    
    cout << "\nCreating critter farm.\n";
    Farm myFarm(3);
    
    cout << "\nAdding three critters to the farm.\n";
    myFarm.Add(Critter("Moe"));
    myFarm.Add(Critter("Larry"));
    myFarm.Add(Critter("Curly"));
    
    cout << "\nCalling Roll...\n";
    myFarm.RollCall();
    return 0;
}



I found this code in a book and it isn't really that well explained in there. I don't suppose somebody could explain it step by step?

Thank you.
It appears that your program is demonstrating the ability of assigning default values to function arguments.

It starts out with includes and "using namespace std". If these need explanation, I would suggest a quick Google search.
Next you have two classes, Critter and Farm. Lets start with the Critter class.
1
2
3
4
5
6
7
8
class Critter
{
public:
    Critter(const string& name = ""): m_Name(name) {}
    string GetName() const { return m_Name; }
private:
    string m_Name;
};

This is the class definition. It starts out with a constructor function. It accepts a string, in the form of a constant, then assigning it to the private variable m_Name. Taking a look into 'cont string& name = ""', this defines once again a constant string, a reference I believe. This is followed by '= ""'. This allows you to call this function omitting this argument. If this is done, it will effectively assign "" to the constant string name.
The next line is somewhat self explanatory. It returns the value of the private string m_Name.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Farm
{
public:
    Farm(int space = 1) { m_Critters.reserve(space); }
    void Add(const Critter& aCritter) { m_Critters.push_back(aCritter); }
    void RollCall() const
    {
        for (vector<Critter>::const_iterator iter = m_Critters.begin();
             iter!= m_Critters.end(); ++iter)
            cout << iter->GetName() << " here.\n";
    }
private:
    vector<Critter> m_Critters;
};

The next class is of a similar concept. The constructor has an int named space whose default value is 1. This allows the calling function to omit this argument. For example, calling "Farm();" would create a farm whose space is equal to 1, while "Farm(2);" would create a farm whose space is equal to 2.
This class has a private vector. This is similar to an array, but more intuitive. If you need an explanation on this, again please refer to Google. The Add function adds a Critter to the vector, and RollCall prints out all the Critters in the vector.
The main function simply creates instances of both classes for demonstration purposes.
So that & in string references a value within what exactly? Also I'm not exactly sure what int space is there for either.
Last edited on
The space parameter on the constructor is used to reserve storage space in the m_Critters vector. Vectors are very efficient, and to help with that they can be told how much space they'll need so they can allocate it all in one go, instead of bit by bit every time you add an entry to the vector. That way there's less work for it to do.

If we break down the constructor:

Farm(int space = 1) { m_Critters.reserve(space); }

The body of the constructor consists of a single call the reserve method of the vector object m_Critters, which actually reserves the space you're going to need. It is passed the value of space which is the parameter supplied when you call the constructor. If you don't supply a value, a default value of '1' is used. In this way, if you know you're going to be adding 50 critters to the farm, you can create a Farm object:

Farm smallFarm(50);

It's not limited to 50, it just allocates enough for 50 straight-up. So the first 50 critters you add will be more efficient. After that, each addition requires the allocation of more memory, so is slower.

Cheers,
Jim
The & placed after a type name specifies that you're defining a reference to an object.

1
2
Critter frankTheCritter("Frank");
Critter& aCritter = frankTheCritter;


In the above code, we have a Critter object called frankTheCritter which we have constructed by calling the Critter constructor and passing it a name string "Frank".
We then create a reference to a Critter called aCritter and assign it to frankTheCritter.

So now we can use aCritter wherever a Critter object is expected and it will refer to the value within frankTheCritter.

The following two lines both output the name of frankTheCritter.

1
2
cout << frankTheCritter.GetName() << endl;
cout << aCritter.GetName() << endl;


It's useful because you now don't have to pass around whole objects, you can pass a reference to it instead, which is much more efficient (because they're smaller). Imagine you had an object that held a 5-minute HD video clip. That would be pretty big and passing that to a function would take a while (to copy the actual memory). Passing a reference to it, however, is a minor operation because references are 4-or-so bytes long - nothing!

Hope that helps!

Jim
Yes I get it in that example but I don't get it in terms of how it's used in the program above.

I don't suppose you could explain to me in that code up there how it works? Also, masterfully explained for the reserve method!
OK, the main() function creates a Farm object myFarm.

It then adds three critters to myFarm.

If we look at the first example,

myFarm.Add(Critter("Moe"));

There's quite a lot of subtle stuff going on, despite the simplicity of the statement.

We're calling the Farm::Add() method on the myFarm instance. In that call we're actually creating a temporary Critter instance - Critter("Moe") and passing it to the call. As an aside, after the whole line has finished, that Critter instance will be destructed and will no longer exist.

If we look at the other end of the call, the definition of the Farm::Add() method,

void Add(const Critter& aCritter) { m_Critters.push_back(aCritter); }

That is saying that it's a function called Add, returning nothing (void) and expecting a reference to a Critter object - that reference variable being called aCritter. Ignore the 'const' bit, I'll come back to that.

So in our call to Add above, the compiler sees the expected reference at the definition and passes a reference to the temporary Critter object.

The body of the Add method calls the push_back method on the m_Critters vector, which adds an object to the end of the vector. In this case, we're telling it to add aCritter, which is a reference to our temporary object created in the call in main.

So we're adding "Moe" to the vector. More subtle stuff - we're actually adding a copy of "Moe" because of the way vector works internally, but you can ignore that for now.

Coming back to the 'const' bit, that just says that the reference is to a const Critter, and is a promise that we're not going to alter the contents of the object.

Cheers,
Jim
The thing I don't get is this: if I remove the & operator in both instances of where it's placed in the program I get the exact same results as if it were still embedded into the program.

Why is that?
Last edited on
If you remove the & so it reads void Add(const Critter aCritter) { ... } it means that Add() expects an actual Critter object, not a reference to one. This is called passing by value. It's what I was talking about when I mentioned objects containing video.

On the surface it's still working the same, but at the point you make the call to Add():

myFarm.Add(Critter("Moe"));

1. Critter("Moe") constructs a Critter object - a temporary one
2. That temporary object is then copied to another temporary one, which is the one defined in the Add() definition above - Critter aCritter
3. Once Add() is done and returns, its temporary aCritter object is destructed
4. Once the call to Add() returns to main(), that temporary (unnamed) Critter object is also destructed.

Steps 2 and 3 are extra, as we're passing the Critter by value.

Jim
Much obliged. Thank you.

I've got another code if people could help me out on:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Friend Critter.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

class Critter
{
	// make following global functions friends of the Critter class
	friend void Peek(const Critter& aCritter);
	friend ostream& operator<<(ostream& os, const Critter& aCritter);

public:
	Critter(const string& name = ""): m_Name(name) {}

private:
	string m_Name;
};

void Peek(const Critter& aCritter);
ostream& operator<<(ostream& os, const Critter& aCritter);

int _tmain(int argc, _TCHAR* argv[])
{
	Critter crit("Poochie");

	cout << "Calling Peek() to access crit's private data member, m_Name: \n";
	Peek(crit);

	cout << "\nSending crit object to cout with the << operator:\n";
	cout << crit;

	return 0;
}

// global friend function which can access all f a Critter object's members
void Peek(const Critter& aCritter)
{
	cout << aCritter.m_Name << endl;
}

//global friend function which can access all of Critter object's members
// overloads the << operator so you can send a Critter object to cout
ostream& operator<<(ostream& os, const Critter& aCritter)
{
	os << "Critter Object - ";
	os << "m_Name: " << aCritter.m_Name;
	system("Pause");
	return os;
}


In this code above, I have no idea how operator overloading works. I understand "friend" but not the overloading.

Help :P!

EDIT: I think I kind of get it though. ostream is basically a friend of Peek thus ostream can access the newly added object (Poochie) so that it can be printed out onto the screen. The "<<" overloaded operator gets it straight from Peek and then prints it out onto the screen with ostream and Peek working together. Am I right or wrong?
Last edited on
Not quite, but close ...

In the definition:

1
2
3
4
5
6
class Critter
{
	// make following global functions friends of the Critter class
	friend void Peek(const Critter& aCritter);
	friend ostream& operator<<(ostream& os, const Critter& aCritter);
}


There are two functions, 'Peek' and 'operator<<'. Peek returns nothing (void) and operator<< returns a reference to an ostream (ostream&).

These two functions are declared 'friend', which means they can access members of the class they're in - in this case Critter.

And just to be clear, it's not ostream that's the friend of Critter, ostream is the return type - it's the operator<< function (that returns the ostream&) that's the friend.

So, when the main function calls

Peek(crit);

the Peek function has full access (as a friend) to the Critter object it's been passed.

Likewise, cout << crit; is calling the global operator<< function, which also has full access as a friend.

Jim
Okay so what does ostream do exactly and again where is & referencing to? The more examples I see the better and quicker I'll get it :).
ostream is a class for writing output to a stream. cout is a stream - the standard output stream which defaults as outputting to your console. An ostream object can write to it.

Any type with an & after it means "reference to that type".

In Peek(), aCritter is a reference to a Critter object.
The use of it at Peek(crit); means that aCritter in Peek is then referencing crit.

Cheers

Jim
Topic archived. No new replies allowed.