C++ Basic Class Constructor - c++

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 : )

Related

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!

C++ Passing a string into a constructor just passes "" unless explicitly modified

I have this class people
class people {
public:
string name;
int balance;
people(string name, int newb) {
name = name;
balance = newb;
}
};
I thought I had a decent understanding of constructors until I couldn't figure out why this wouldn't work:
for(int i=0; i<users; i++) {
fin>>temp_name; // !!!!!
givers_list.push_back(temp_name, 0);
}
No error, only when I'm debugging I have a vector of users that all have names = "" and balance = 0; I am also 100% sure that temp_name is a string like "David"
Only when I make a temporary object, "cat", construct it, modify it's name, and then push_back does it work right:
for(int i=0; i<users; i++) {
fin>>temp_name; // !!!!!
people cat = people(temp_name, 0);
cat.name = temp_name;
givers_list.push_back(cat);
}
Why didn't it work the first time? I've checked SO and C++ forums and I believe they did what I did. Curiously, I'm sure the issue is regarding a string because when I say givers_list.push_back(people(temp_name, 50));, then the object would correctly have 50 as a balance (name is still "").
My background started with C++ and I've done Java for a long time but returning to C++ is a little alien. Sorry if this is a stupid mistake. I already finished and submitted the program I just don't know why the constructor doesn't work without a stent.
Let me know if this replicates for you.
The problem is that your constructor is incorrect.
Inside:
people(string name, int newb) {
name = name;
balance = newb;
}
name refers to the parameter name, not the class data member name.
To fix this, either qualify name with this, or directly construct name and balance, rather than default-constructing them and then assigning to them:
//Option 1:
people(string name, int newb) {
this->name = name;
balance = newb;
}
//Option 2: Preferred; avoids duplicate initialisation.
people(string name, int newb) :
name(name), balance(newb)
{
}
//Option 3: Optimal; avoids unnecessary copies.
people(string name, int newb) :
name(std::move(name)), balance(newb)
{
}
This line
name = name;
The compiler is using the parameter name for each part of this statement. This means it is assigning the parameter name to itself.
Change it to
this->name = name;

c++ class setter sets variables different when argumentname and variable name are equal

I'm completly new to c++ and I found something I don't understand and which I cannot find the answer for(also I'm sure it has been asked a lot, so point me to a thread would also be nice).
Example code:
#include <iostream>
class Car {
int doors;
public:
void set_doors(int doors){
doors = doors;
}
int get_doors(){
return doors;
}
};
int main()
{
Car ford;
ford.set_doors(3);
std::cout << ford.get_doors() << std::endl;
}
So when I run the code it returns always an int of length 9.
I know that the issue is happening because in
void set_doors(int doors)
I'm using the same name for the argument and the variable I'd like to change.
If I would change the code to
void set_doors(int newdoors){
doors = newdoors;
}
everything will work perfectly.
My question is:
Why does the code behave like this, when using the name for the variable which I like to modify and the argument name?
Please explain it in an easy way ;)
Thanks
Best
Why does the code behave like this, when using the name for the variable which I like to modify and the argument name?
Because the rules of C++ require it to behave this way: names of local variables and parameters "win" over member variables. You should get a compiler warning on this assignment, saying that the assignment has no effect (it assigns the value of a parameter back to itself).
One of idiomatic ways of resolving this is as follows:
this->doors = doors;
This is the way the language lets you resolve situations when an unqualified name could refer to more than one thing.
Your code has undefined behaviour, because you attempt to read an uninitialized variable. In the following function:
void set_doors(int doors){
doors = doors;
}
doors always refers to the function argument, not to the member variable. In other words, you self-assign the function argument and your doors member variable is left untouched. What's worse is the member variable is never initialized. Reading it in get_doors produces 9 only by sheer coincidence on your machine. The program could just do anything.
You can fix your setter function like this:
void set_doors(int doors){
this->doors = doors;
}
Still, your class would be very easy to use incorrectly, because it is not obvious that one should have to call set_doors before get_doors actually works. You want to initialize doors to 0 in the constructor:
class Car {
int doors;
public:
Car() : doors(0) {}
void set_doors(int doors){
this->doors = doors;
}
int get_doors(){
return doors;
}
};
Once inside the function get_doors, the argument 'doors' hides the member variable 'doors'. So the assignment 'doors = doors' basically assigns the function parameter doors to doors and the member variable is still left undefined.
Also, I would ideally not design the class this way, I would rather setup the class member variables in the constructor.
*
Class Car {
int doors; public:
Car(int doors): doors(doors) {}
int get_door_count(){ return doors; } };
*
Notice that in this case the compiler can correctly differentiate between the function argument doors and the member variable doors when using the member initialiser syntax.

Is it bad to have the same name for parameter as for member variable? [duplicate]

This question already has answers here:
Should I use the same name for a member variable and a function parameter in C++?
(10 answers)
Closed 9 years ago.
For example, is this any of the following
Bad practice
Unreadable
Inefficient (the call to this pointer)
Any other reason why it's bad to do this
.
class Person {
public:
string name;
Person(string name) {
this->name = name;
}
};
P.S.
How about Person(string name) : name(name) { }
No, I don't think this is a bad way to do so. Sometimes we even face the same method name or property name from different libraries. That's why we create namespace and class to resolve the naming conflict.
As long as it will not result in confusion, you should make it as simple as possible. Even though they use the same name. However, you shouldn't mix them, for example:
class Person {
public:
Person(name) {
this->name = name;
name = clean(this->name);
this->name = prefix + name;
}
private:
string name;
};
Keep it clean:
class Person {
public:
Person(name) {
name = clean(name);
name = prefix + name;
this->name = name;
}
private:
string name;
};
The only issue(not a real issue) I can think of is that you can't distinguish member variable with local variable or function parameter. It's just coding style, it's nothing to do with efficiency, but when you talk about Unreadable, that's yes for me.
For me I normally name class member variable with trailing underscore. It helps code readability and makes it easier for maintenance.
class Person {
public:
string name_; // member variable with traling `_`
string m_surname; // some microsoft style declares member start with `m_`
Person(const string& name) // pass parameter by reference.
: name_(name) // you know you are constructing member name_ with name variable
{
}
};

Copy string value into a class field?

I'm new to and learning C++. I know a fair amount of Java and some C.
What I want to do is to create an immutable name class that takes in a string value, copies that string to a class field and then eventually hashes it to an ID that can be parsed much more efficiently than a string.
I'm hitting a wall due to a general lack of knowledge of C++ strings. Here's what I have so far...
#pragma once
#include <string>
class Name
{
public:
Name(std::string s);
~Name(void);
int getId();
std::string getName();
private:
int id;
std::string name;
};
and...
#include "Name.h"
Name::Name(std::string s)
{
}
So what I want to do is store the value of s, passed in by the constructor in the "name" private field. As far as I know a new string object must be created and then the value of s must be copied into it.
I also think that the argument s can and should be a string pointer instead of a string object (to prevent an unnecessary copy from occurring). If I'm right then the constructor should look like the following, right?
Name::Name(std::string &s) { ... }
In this case, nothing would need to be done special when passing in a name? IE.
Name n = new Name("Cody");
is perfectly valid? Actually I'm not sure since "Cody" to my knowledge is a constant string or something like that.
So if I'm all on the right track, then what is the proper way to actually copy the value? I'm thinking this is appropriate but I'm not sure.
#include "Name.h"
Name::Name(std::string s)
{
name = new string(s);
}
Thanks for the help in advance, I know it's a basic question but I'm slowly making baby steps into the C++ world. :) - Cody
You are close, your code can be like this after a little massage:
class Name
{
public:
Name(const std::string& s); // add const and reference
~Name(void);
int getId() cosnt; // add const
std::string getName() const; // add const
private:
int id;
std::string name;
};
Name.cpp
Name::Name(const std::string& s):name(s)
{
}
Here :name(s) is called member initializer list.
Name n = new Name("Cody"); is perfectly valid? Actually I'm not sure
since "Cody" to my knowledge is a constant string or something like
that.
No, n is not pointer, it's not like java you need to new for every object. In C++, you do
Name n("Cody");
This will call Name(const std::string& s) to initialize object n and initialize name string with "Cody".
Note: variable n has automatic storage duration, it will be destroyed if it goes out of scope.
To let n on dynamic storage duration, you need to use new/delete pair:
Name *pn = new Name("Cody");
delete pn;
or use smart pointers, you no need to call delete n_ptr; as n_ptr will be destroyed when it goes out of scope as well:
#include <memory>
std::shared_ptr<Name> n_ptr(new Name("Cody"));
EDIT:
To use Name class in other classes, it's the same way when you use string in Name class, you don't have to use pointers.
class TestName
{
public:
TestName(const Name& n):name_(n){ }
private:
Name name_;
};
TestName tn("Cody");
You should use a constant reference to std::string here.
As you said, it would prevent unnecessary copies.. But then why not just a pointer or a constant pointer?
A constant reference would allow you to pass to your function some arguments that would implicitly call the right std::string constructor.
So, in a nutshell, you could do that:
Name::Name(const std::string& s)
{
this->name = s;
}
// Or even better..
Name::Name(const std::string& s):
name(s)
{
}
int main(void)
{
Name nick("hello");
return 0;
}
You can find out about every std::string's constructors on its cplusplus.com's sheet.