Create an instance of an object C++ - c++

I have recently picked up C++ but I am having problems in some areas as I am used to Java and I am not sure if I am going about things the right way.
I have a text file which lines are structured like this
C;101;1;1;4;10;
H;102;9;0;1;8;2
C;103;9;9;2;5;
C;104;0;9;3;8;
; being the delimeter
C is used to check if its a Crawler(H is used to check if it is a Hopper), it is then followed by the id, positions, directions(1-4), size, and alive(0/1)
I have an abstract Bug class then I have a Crawler and Hopper class which both inherit from the Bug class.
Here is my Bug class
class Bug
{
public:
int id;
pair<int, int> position;
int direction;
int size;
bool alive;
list<pair<int, int>> path;
virtual void move() {}
bool isWayBlocked() {}
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
{
type = type;
id = id;
position = position;
direction = direction;
size = size;
alive = alive;
};
};
Here is the Crawler class
class Crawler : public Bug
{
public:
Crawler(int id, pair <int, int> position, int direction, int size)
: Bug("Crawler", id, position, direction, size, alive)
{
}
};
Here is an example of me trying to create an instance of a Crawler object:
Bug* bugPtr;
if (bugType == "C") {
bugPtr = new Crawler(bugID, { xPos,yPos }, direction, size);
//Crawler* CrawlerBugPointer = new Crawler(bugID, { xPos,yPos }, direction, size);
}
I am able to easily read from the text file and store the variables but when i start debugging all of the fields in the bugPtr say 0, I suspect that I have gone about things in completely the wrong way and have not built the Abstract class and Inheritor classes properly, could anyone point out what I am doing wrong? any help will be much appreciated.

The compiler should have generated a warning, "Expression with possibly no effect" (or similar), for every line in your constructor. Read the warnings - they're useful.
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
{
this->type = type;
this->id = id;
this->position = position;
this->direction = direction;
this->size = size;
this->alive = alive;
};
While C++ doesn't require the ridiculous use of this everywhere, in your case it's necessary since you've given the constructor parameters the same names as the fields. When you enter something like
id = id;
You're saying, "Find the variable with name id in this scope, and assign its value to itself." Which does nothing, which is why all of your fields were left at their default values. Saying this.id means, "Find the class field with name id and assign the value of the local id to it."

The way to do this is with an initializer list:
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
: type(type), id(id), position(position), direction(direction), size(size), alive(alive)
{ }
The problem with the original code is name hiding. In the constructor body, type = type simply assigns the value of the argument named type to the argument named type. It doesn’t change the value of the member named type.
But in an initializer list, in an entry like type(type), the first type is the name of the member and the second type is the name of the argument. So the member gets the argument value, and all is well.
You should always use initializer lists in constructors. In addition to allowing duplicate names (if that’s what you like; many programmers don’t) they are more efficient when you have data members with more complex initialization.

Related

Deleting an object from a dynamically allocated array in C++

In the first stage, i've created an object Planet with some attributes, like name, type and distanceToEarth. I've created a Repository then, basically a structure consisting of a dynamic array elems and its length and maximum capacity.
typedef enum {
NEPTUNE_LIKE,
GAS_GIANT,
TERRESTRIAL,
SUPER_EARTH,
UNKNOWN
}PlanetType;
typedef struct {
char name[30];
PlanetType type;
float distanceToEarth;
}Planet;
Planet createPlanet(char name[], PlanetType type, double distance) {
Planet pl;
strcpy(pl.name, name);
pl.distanceToEarth = distance;
pl.type = type;
return pl;
}
typedef struct
{
Planet* elems; /** dynamic array containing the planets */
int length; /** actual length of the array */
int capacity; /** maximum capacity of the array */
} PlanetRepo;
PlanetRepo createPlanetRepo(int capacity) {
/// create a new planet repo; the elems field must be dynamically allocated (malloc)
PlanetRepo r;
r.capacity = capacity;
r.length = 0;
r.elems = (Planet*) malloc(sizeof(Planet)*capacity);
return r;
}
bool remove(PlanetRepo* repo, Planet pl) {
/// #todo remove planet pl from the repository
/// return false if the planet does not exist in the repository
return false;
}
My problem is related to the function remove(). I can't figure out how I am supposed to remove that object from a dynamically allocated array.
Of course, this is not the entire code, but I've selected only the relevant parts. If I forgot to include something, let me know.
Since you insisted on tagging C++, rather than C:
In C++ you wouldn't define the PlanetRepo and the associated functions at all. Instead you would simply declare a variable of type
std::vector<Planet>
or maybe depending on the use case (but less likely)
std::list<Planet>
Both of these already have member functions .erase that remove elements from them.
In C++ you also wouldn't write
typedef struct {
char name[30];
PlanetType type;
float distanceToEarth;
}Planet;
but instead
struct Planet {
char name[30];
PlanetType type;
float distanceToEarth;
};
and you would most likely use std::string instead of char[30] as type for name.
Instead of a function Planet createPlanet(char name[], PlanetType type, double distance) you would define a constructor for Planet:
struct Planet {
std::string name;
PlanetType type;
float distanceToEarth;
Planet(std::string name, PlanetType type, double distance)
: name(name), type(type), distance(distance)
{}
};
and probably make the members private.
You also wouldn't define an unscoped enum, but a scoped one instead (since C++11), see Why is enum class preferred over plain enum?.
Since this is rather a C than a C++ program, you could use a linked list which makes it possible for you to delete elements in a dynamically allocated "array".
This might be of interest.
Like mentioned before C++ has implemented data structures so you can easily store your planets. But you can do something like:
bool remove(PlanetRepo* repo, Planet pl) {
PlanetRepo* temp = (PlanetRepo*) malloc(sizeof(Planet)*repo->capacity);
if(!temp){
return false;
}
temp->capacity = repo->capacity;
temp->size = repo->size-1;
for(int i = 0; i < repo->size; ++i){
if(repo[i] != pl){ //This won't work. How to compare Planets is left as an exercise to the reader
temp[i] = repo[i];
}
}
free(repo);
repo = temp;
return true;
}

Creating a counter inside a template class constructor

I'm stuck on a homework question. We are to create a class template called department, and in the constructor, we need to initialize a counter to be used later. I'm having trouble understanding how to use this counter elsewhere in the program. We were provided with a main.cpp file to use, which we aren't allowed to change. These are the specific instructions I'm stuck on:
You are to create a constructor that may take the department name as an argument, and if it’s null it will ask for a department name to be entered from the keyboard and stores it. It also initializes a counter that keeps track of the number of employees in the array and is maintained when you add, remove, or clear.
The only way I've managed to get it to work is by setting the constructor to accept two arguments, one for department name, and one for the counter. But the main.cpp file provided only allows for one argument, name.
Department.h:
template <class Type>
class Department {
private:
std::string name;
...
public:
Department(const std::string & deptName)
{
int counter = 0;
name = deptName;
}
...
};
Main.cpp (provided, and not allowed to change):
int main()
{ Department dept1("CIS"); // a department
...
Is there a way to use the counter initialized in the constructor outside of the constructor without changing the argument requirements for Department?
Is there a way to use the counter initialized in the constructor outside of the constructor without changing the argument requirements for Department?
Sure. Make a counter member variable, and use it in the methods you write for your class.
template <class Type>
class Department {
private:
std::string name;
int counter;
public:
Department(const std::string & deptName)
{
counter = 0; // note `int` not needed, as counter is already declared
name = deptName;
}
int getCounter()
{
return counter;
}
void addEmployee(std::string name)
{
counter++;
// do something with adding employees
}
// other methods
};

C++ Basic Class Constructor

I'm going through a C++ course right now, and they're doing a good job of over-explaining everything until it comes to splitting the files up between header "promises" and implementation. I learned programming entirely in Python so I'm used to just declaring things outright and importing the class as necessary, so this whole separating a promise and then implementing logic is strange to me.
Anyway, I'm having trouble because in the course they were saying you never actually need to use this-> but when I'm trying to define a class explicitly in the same .cpp file, I can only get the constructor to work when I use this->. Can someone please explain or link a discussion that explains this? I haven't been able to find a reference that explains this issue in the context of defining everything in the same file.
class Person {
public:
string name;
int age;
int height;
int weight;
Person (string name, int age, int height, int weight) {
name = name;
age = age;
height = height;
weight = weight;
}
};
int main () {
Person new_person("Doug", 20, 70, 170);
}
This causes all the values I pass in from the main function to not initialize. However, if I add this-> to each line in the constructor, it works fine. The examples I find don't use this-> so I'm confused why it's necessary here. Perhaps it has to do with namespaces which are still a little confusing to me (the whole using namespace std; thing) but I thought since it's all in the same .cpp file, this should work.
The argument variables overshadow your member variables in the scope of your constructor. That is, within that context, name refers to the input variable only.
What you could do is use an initialization list:
Person (string name, int age, int height, int weight) :
name(name), age(age), height(height), weight(weight) {}
Or use different names:
Person (string _name, int _age, int _height, int _weight) {
name = _name;
age = _age;
height = _height;
weight = _weight;
}
But your approach using this-> is totally fine.
You might also want to read http://www.cs.technion.ac.il/users/yechiel/c++-faq/using-this-in-ctors.html
In this case you need this because you are using the same name for both your constructor parameters and class members.
Person (string name, int age, int height, int weight) {
this->name = // this-> means the member variable
name; // just the parameter
If you had a different name for your parameter this would not be necessary
Person (string name_, int age, int height, int weight) {
name = // the member variable
name_; // the parameter
Only problem with ->this approach is that it is A) wordy b) doesn't initialize object. c) makes this object non-trivial.
Proper initializing constructor would look like this, using initialization list:
Person (string nm, int ag, int ht, int wt):
name(nm), age(ag), height(ht), weight(wt) {}
It's not really same as doing what you do, in this case object can be created statically without performing any actions. Your implementation of class always performs assignments. C++11 and later allows to do away with fully trivial implementation:
class Person {
public:
string name;
int age;
int height;
int weight;
void do_stuff() {};
};
// let's assume that string is std::string,
// creation from const char* and = are defined
int main()
{
Person p = { "Nemo", 35, 5, 120 };
Person p2 = p;
p = { "John-117", 24, 6, 170 };
}
What you have is a case of ambiguity of dependent names. See here:
If the lookup of a member of current instantiation gives a different
result between the point of instantiation and the point of definition,
the lookup is ambiguous. Note however that when a member name is used,
it is not automatically converted to a class member access expression,
only explicit member access expressions indicate members of current
instantiation:
Eli Bendersky writes eloquently about this. An excerpt:
All you have to do is to make the compiler understand that the call f depends on the template parameter T. A couple of ways to do this are replacing f() with Base::f(), or with this->f() (since this is implicitly dependent on T).
So your own this-> solution is more than fine : )

struct with enum compilation error based on position

This:
enum Id {
START,
INDENT
};
struct State {
int line;
int column;
Id id;
};
...
lexer::State state = {0};
compiles.
But if I put Id id as the first element of the struct, it stops. Can someone explain to me briefly why these two structs are treated differently.
You get an error because constant zero of type int is not compatible with the initial field id of type Id, unless
1) You add a cast, like this
lexer::State state = {static_cast<Id>(0)};
2) or use START in place of zero, like this
lexer::State state = {START};
3) or drop zero to value-initialize the whole struct (recommended):
lexer::State state = {};

having an array as class attribute

sorry i'm new to c++ and i feel a little stupid for asking, but i can't get it to work after hours of googling and i can't find what i'm doing wrong
the task is pretty easy:
i want to have employees that have name, age etc (those work)
and i want these employees to be stored inside an array.
so i have:
class Employee {
public:
Employee(string, string, int, int);
string name;
string firstName;
int birthYear;
int usedVacation;
int getAge();
};
and then the list:
class EmployeeList {
public:
int addEmployee(string, string, int, int);
Employee eList[500];
};
what i want is: an object that holds an array of Employees, with methods to add/edit etc them.
so i define the addEmployee method outside the class as follows:
int EmployeeList::addEmployee(string first, string last, int year, int used) {
int i = 0;
for (i; i < this.eList.length; i++) {
if (this.eList[i].deleted == true) {
this.eList[i] = {
firstName: first,
lastName : last,
birthYear : year,
usedVacation : used,
deleted : false };
} else {
this.eList.push({ firstName: first, lastName : last, birthYear : year, usedVacation : used, deleted : false });
}
return i;
}
};
As you probably instantly see, there's a lot wrong with that.
All of the this throw Expression must have class type in VS2015,
also identifier firstName is undefined and identifier lastName is undefined,
but in my eyes, there's nothing wrong with my code.
i'm pretty sure this is a very basic thing i just didn't get yet, but i just can't find out, where the problem is.
I defined methods outside Employee, and this works there without a problem (although with this->, but i tried that and it doesn't work either)
please forgive my lack of skills, i come from javascript :(
this.eList[i] =
this is a pointer, not a reference, so you access the members via this->eList. But 99.99% of the time, it's completely unnecessary. You're inside a member method, so the compiler knows what this is. All you need is eList[i] =.
this.eList.push({
First a brief tutorial with arrays in C++. Employee eList[500] doesn't make an array that can hold 500 Employees, eList is 500 Employees. Forever and always. They are are instantly constructed with the default constructor (or will be once you give Employee a default constructor. This is required if you want to have objects in an array, except in very advanced cases).
this.eList.length
There are three actions you can do with arrays: you can access an element with the [i] syntax, they magically convert to a pointer to the first item very often, and finally, if and only if they're a member of a struct, they can be copied. That's all you can do with them. There is no push, because as I said, it is 500 Employee. There is no length member. Yours is always 500 Employees.
if (this.eList[i].deleted == true)
You gave each Employee 4 members, deleted was not one of them. Instead of keeping track of which Employee objects are unused, the normal thing is to keep all the employees in the first few slots, and give EmployeeList a count to keep track of how many "live" employees there are. The live ones are in indecies 0-count. The employees at count-500 are "deleted". Unfortunately, this does mean that when one is deleted, you have to shift forward all the ones after that.
this.eList[i] = {
firstName: first,
lastName : last,
Employee does not have a lastName. The right way to construct an Employee is via the constructor you declared: Employee(string, string, int, int). So that line should probably be eList[i] = Employee(first, last, year, used);. This creates a new Employee object, and then copies it into that slot of the array. Alternatively, just assign the members one by one.
How about something like:
#include<string>
class Employee
{
public:
std::string firstname;
std::lastname;
int birthyear;
int usedVacation;
void setFirstname(std::string fname){firstname = fname;}
void setLastname(std::string lname){lastname = lname;}
void setBirthyear(int year){birthyear = year;}
void setVacation(int vac){usedVacation = vac;}
};
class EmployeeList
{
public:
int count;
EmployeeList eList[500];
EmployeeList()
{
count = 0;
}
void addEmployee(std::string fname, std::string lname, int year, int vac);
void removeEmployee(std::string fname, std::string lname, int year, int vac);
};
void EmployeeList::addEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count++;
}
void EmployeeList::removeEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count--;
}
BTW I am not sure where your push, deleted, and length methods came from? You will likely also want to add more methods then what I have written, for example a method to return count. You can also add get methods to the employee class and make your member variables private. I hope this is of some help. Goodluck!