principlecppMinor
Doodlebug vs. ant population simulation
Viewed 0 times
simulationantdoodlebugpopulation
Problem
I am looking for a review on one of my homework assignments for this semester.
This homework has already been submitted and graded and my final has already been submitted so there is no cheating or conflict of interest in my review request!
I would love some advice on how to better manage my class interactions and how to better encapsulate data. The specific area I struggle with is when using parent classes and giving access to only protected assets from child classes. My professor has mentioned on several occasions that it would be much better to keep data private and give access through functions, but how would I initialize those member variables when instantiating an instance of a child class that has private members in the parent class?
I understand the idea of encapsulation is to protect data that shouldn't be manipulated by outside programmers or irrelevant classes. I was the sole developer on this project so I understand in this specific example encapsulation may not be paramount, but on a larger project with multiple engineers it would certainly be relevant.
Includes
** Note the coordinates struct just contains an integer xCoordinate and an integer yCoordinate
main
Environment
```
class environment
{
//friends of environment
friend class organism;
friend class doodlebug;
friend class ant;
private:
organism * environmentBoard[20][20];
void CreateStartPopulation();
int GenerateRandomStartingLocations(int min, int max);
void OutputCurrentEnvironment();
void DoodlebugsAct();
void AntsAct();
void ResetCritterTimeStep();
public:
//constructor
environment();
//deconstructor
~environment();
//public member functions
void Initializ
This homework has already been submitted and graded and my final has already been submitted so there is no cheating or conflict of interest in my review request!
I would love some advice on how to better manage my class interactions and how to better encapsulate data. The specific area I struggle with is when using parent classes and giving access to only protected assets from child classes. My professor has mentioned on several occasions that it would be much better to keep data private and give access through functions, but how would I initialize those member variables when instantiating an instance of a child class that has private members in the parent class?
I understand the idea of encapsulation is to protect data that shouldn't be manipulated by outside programmers or irrelevant classes. I was the sole developer on this project so I understand in this specific example encapsulation may not be paramount, but on a larger project with multiple engineers it would certainly be relevant.
Includes
#include "stdafx.h"
#include
#include
#include
using namespace std;** Note the coordinates struct just contains an integer xCoordinate and an integer yCoordinate
main
int main()
{
//Create environment object containing
environment antDoodlebugSimulation;
antDoodlebugSimulation.InitializeSimulation();
return 0;
}Environment
```
class environment
{
//friends of environment
friend class organism;
friend class doodlebug;
friend class ant;
private:
organism * environmentBoard[20][20];
void CreateStartPopulation();
int GenerateRandomStartingLocations(int min, int max);
void OutputCurrentEnvironment();
void DoodlebugsAct();
void AntsAct();
void ResetCritterTimeStep();
public:
//constructor
environment();
//deconstructor
~environment();
//public member functions
void Initializ
Solution
Do not use namespace std;
This is a bad habit, that might bring you into trouble. You might want to search for it.
Avoid magic numbers
For example your board size is 20. And all over the code there is a 20 sprinkled in. What happens when you change the board size and forget one of those locations. Better create a member variable bordSize of the environement class and use that throughout the code. This makes it much more readable.
Same goes for stuff like this
Create enumerations like
Then you can do
Use magic numbers consistently
You have 2 magic numbers in your code 19 and 20. But wait, they are the same. The difference is, that you
Avoid using this-> inside a class
Use consistent braces
The second for loop has no braclet and whats wore it holds a single line statement that goes over 4 lines. This is bound to introduce bugs in the long run. Also in such simple if else constructs you should rather use the ternary operator
Avoid flushing cout
Avoid meaningless comments
The above comment
Avoid endless lines
Readability is key. Whenever you have to scroll left or right your lines are too long. Most open source project go for 80 characters which is quite sensible.
Use base class constructors when appropriate
You can defer a derived class constructor to the base class constructor
https://stackoverflow.com/questions/6923722/how-do-i-call-the-base-class-constructor
Simplify if/else with boolean return
You have some
Same goes for assignments
Is equivalent to
You might want to use lambda functions
A lot of your code is incredibly repetitive. For example these lines appear extremely often
Afterwards, what you do inside the code is mostly small and well contained. Here you could define lambda functions and pass them as arguments to a function that just iterates over the loops and then calls the function
https://stackoverflow.com/questions/3203305/write-a-function-that-accepts-a-lambda-expression-as-argument
https://stackoverflow.com/questions/8109571/lambda-as-function-parameter
This is a bad habit, that might bring you into trouble. You might want to search for it.
Avoid magic numbers
For example your board size is 20. And all over the code there is a 20 sprinkled in. What happens when you change the board size and forget one of those locations. Better create a member variable bordSize of the environement class and use that throughout the code. This makes it much more readable.
Same goes for stuff like this
//North
if (direction == 1)Create enumerations like
enum direction {
NORTH,
SOUTH,
EAST,
WEST
};Then you can do
if (direction == NORTH)Use magic numbers consistently
You have 2 magic numbers in your code 19 and 20. But wait, they are the same. The difference is, that you
GenerateRandomStartingLocations() is inclusive. Again, there is no reason, why this function should take arguments at all. create a member variable and use that inside of GenerateRandomStartingLocations()Avoid using this-> inside a class
this-> is only necessary if you have name collisions. It is always better to simply not have name collisions rather than using this->Use consistent braces
void environment::OutputCurrentEnvironment()
{
//Outputs current environment to the screen
for (int yCounter = 0; yCounter environmentBoard[xCounter][yCounter] == nullptr)
cout << '-';
else
cout << *environmentBoard[xCounter][yCounter];
cout << endl;
}
}The second for loop has no braclet and whats wore it holds a single line statement that goes over 4 lines. This is bound to introduce bugs in the long run. Also in such simple if else constructs you should rather use the ternary operator
void environment::OutputCurrentEnvironment()
{
//Outputs current environment to the screen
for (int yCounter = 0; yCounter < boardSize; yCounter++)
{
for (int xCounter = 0; xCounter < boardSize; xCounter++)
{
std::cout << environmentBoard[xCounter][yCounter] == nullptr ? '-' : *environmentBoard[xCounter][yCounter] << "\n";
}
}
}Avoid flushing cout
std::endl flushes the stream which is nearly always unneeded, so use a simple new line "\n"Avoid meaningless comments
The above comment
//Outputs current environment to the screen doesnt add anything to the code. So just omit it. Avoid endless lines
Readability is key. Whenever you have to scroll left or right your lines are too long. Most open source project go for 80 characters which is quite sensible.
Use base class constructors when appropriate
You can defer a derived class constructor to the base class constructor
https://stackoverflow.com/questions/6923722/how-do-i-call-the-base-class-constructor
Simplify if/else with boolean return
You have some
if condition return true else false You can simpify that to return condition or return !conditionSame goes for assignments
if (this->moveAlternator == false)
this->moveAlternator = true;
else
this->moveAlternator = false;Is equivalent to
moveAlternator = !moveAlternator;You might want to use lambda functions
A lot of your code is incredibly repetitive. For example these lines appear extremely often
for (int yCounter = 0; yCounter < boardSize; yCounter++)
{
for (int xCounter = 0; xCounter < boardSize; xCounter++)
{Afterwards, what you do inside the code is mostly small and well contained. Here you could define lambda functions and pass them as arguments to a function that just iterates over the loops and then calls the function
https://stackoverflow.com/questions/3203305/write-a-function-that-accepts-a-lambda-expression-as-argument
https://stackoverflow.com/questions/8109571/lambda-as-function-parameter
Code Snippets
//North
if (direction == 1)enum direction {
NORTH,
SOUTH,
EAST,
WEST
};if (direction == NORTH)void environment::OutputCurrentEnvironment()
{
//Outputs current environment to the screen
for (int yCounter = 0; yCounter < 20; yCounter++)
{
for (int xCounter = 0; xCounter < 20; xCounter++)
if (this->environmentBoard[xCounter][yCounter] == nullptr)
cout << '-';
else
cout << *environmentBoard[xCounter][yCounter];
cout << endl;
}
}void environment::OutputCurrentEnvironment()
{
//Outputs current environment to the screen
for (int yCounter = 0; yCounter < boardSize; yCounter++)
{
for (int xCounter = 0; xCounter < boardSize; xCounter++)
{
std::cout << environmentBoard[xCounter][yCounter] == nullptr ? '-' : *environmentBoard[xCounter][yCounter] << "\n";
}
}
}Context
StackExchange Code Review Q#163468, answer score: 9
Revisions (0)
No revisions yet.