How to change this->value from constructor with a function inside the same class in C++? - c++

I have a class student and constructor for name and age but latter on student must be able to edit that info.
I tried following but edit info dont work:
class Student{
public:
string name;
int age;
int grades[3];
int assignments[4];
Student(string name, int age){
this->name = name;
this->age = age;
}
void edit_info(){
string newName;
int newAge;
cout<<"Set new name:";
cin>>newName;
cout<<"Set new age:";
cin>>newAge;
this->name=newName;
this->age=newAge;
}
void show_info(){
cout<<name<<"\n";
cout<<age<<"\n";
}
Code when I chose which student to "play":
if(input == "studentOne"){
cout<<"You are now student 1\n";
cout<<"What is your name:";
cin >> s1_name;
cout<<"How old are you:";
cin >> s1_age;
Student student1(s1_name, s1_age);
while(true){
CMD(student1);
}
and CMD function:
void CMD(Student student){
string command;
cout<<"Type your command(edit_info, submit_assignments, sitFortest, show_info):";
cin>>command;
if(command=="edit_info"){
student.edit_info();
}
else if(command=="show_info"){
student.show_info();
}
}

This code
Student student1(s1_name, s1_age);
while(true){
CMD(student1);
}
void CMD(Student student){
...
}
creates a new Student object each time that CMD is called. The object student in the CMD function is not the same as the object student1 in the calling function. So changes to this object have no effect on the object in the calling function.
This is because by default C++ uses call by value, meaning that objects are copied when passed as parameters. If you want to avoid this happening then you must use references.
The change is very simple
void CMD(Student& student){ // & means use call by reference
The extra & turns student into a reference to the calling variable, instead of a separate object. Now changes to student will actually change the object in the calling function, instead of changing a copy of the object in the calling function.
This is a common misunderstanding among beginners. Some languages do use call by reference, but in C++ the default is call by value.

To initialize member variables, give members a distinct naming scheme. (There are a few conventions around, I am used to prefixing with m_).
And have a look at this : https://en.cppreference.com/w/cpp/language/constructor member initialization lists.
#include <vector>
#include <string>
#include <iostream>
class Student
{
public:
Student(std::string name, unsigned int age) :
m_name{ name },
m_age{ age }
{
// don't initialize members in body use member initialization list.
//this->name = name;
//this->age = age;
}
void edit_info()
{
// you can directly input into members.
std::cout << "Set new name:";
std::cin >> m_name;
std::cout << "Set new age:";
std::cin >> m_age;
}
private: // class members should be private!
std::string m_name;
unsigned int m_age; // you don't execpt age to be < 0 so unsigned
std::vector<int> grades;
std::vector<int> assignments;
};

Related

C++ class does not output correct data?

No compilation error but the output is this:
g++ class.cpp && ./a.out
is -1003073000 years old.
It does not output the string and int as it supposed to be.
I don't know what is wrong, I would really appreciate if someone point out my mistake, thanks!.
Here is the code:
#include<iostream>
class Student{
private:
std::string name;
int age;
public:
Student(std::string name, int age){
name = name;
age = age;
}
void setName(std::string name){
name = name;
}
std::string getName(){
return name;
}
void setAge(int age){
age = age;
}
int getAge(){
return age;
}
};
int main(){
Student student1 = Student("Clayton",20);
std::cout<<student1.getName();<<" is "<< student1.getAge()<<" years old."<<std::endl;
}
In the constructor
Student(std::string name, int age){
name = name;
age = age;
}
The names name and age are the argument variables of those names. Which means you assign the variables to themselves.
That will leave the Student::name member default constructed and empty, but the Student::age variable will be uninitialized, and have an indeterminate value, and using such values (even printing them) is undefined behavior.
You have two solutions:
Use this to explicitly reference the current object and use its member variables:
Student(std::string name, int age){
this->name = name;
this->age = age;
}
Or use a member initializer list to initialize (rather than assign to) the member variables:
Student(std::string name, int age) : name(name), age(age) {
// Empty
}
For this the language knows the difference between the member and argument variables.
I highly recommend the second alternative.

Pass data from object in class A to class B

New to classes and objects in c++ and trying to learn a few basics
I have the class TStudent in which the Name, Surname and Age of student are stored, also I have the constructor which is accessed in main and inserts in the data.
What I want to do is: having the class TRegistru, I have to add my objects data in it, in a way that I can store it there, then I could save the data in data.bin and free the memory from the data, then I want to put the data back in the class and print it out.
The question is: In what way & what is the best way to add my objects in the second class, so that I could eventually work with them in the way I've described in the comments, so that I won't have to change nothing in main
Here's my code so far:
#include <iostream>
using namespace std;
class TStudent
{
public:
string Name, Surname;
int Age;
TStudent(string name, string surname, int age)
{
Name = name;
Surname = surname;
Age = age;
cout <<"\n";
}
};
class TRegistru : public TStudent
{
public:
Tregistru()
};
int main()
{
TStudent student1("Simion", "Neculae", 21);
TStudent student2("Elena", "Oprea", 21);
TRegistru registru(student1);//initialising the object
registru.add(student2);//adding another one to `registru`
registru.saving("data.bin")//saving the data in a file
registru.deletion();//freeing the TRegistru memory
registru.insertion("data.bin");//inserting the data back it
registru.introduction();//printing it
return 0;
}
Hence the question is about passing data from A to B, I will not comment on the file handling portion.
This can be done in multiple ways, but here is one of the simplest and most generic. By calling TRegistru::toString() you serialize every TStudent added to TRegistru into a single string which then can be easily written to a file.
Demo
class TStudent
{
public:
std::string Name, Surname;
int Age;
std::string toString() const
{
return Name + ";" + Surname + ";" + to_string(Age);
}
};
class TRegistru
{
public:
void add(const TStudent& student)
{
students.push_back(student);
}
void deletion()
{
students.clear();
}
std::string toString() const
{
std::string ret{};
for(const auto& student : students)
{
ret += student.toString() + "\n";
}
return ret;
}
std::vector<TStudent> students;
};

C++ Null output when a function is called

Below is a snippet of code from my main program
My H file
class Person{
public:
std::string name;
int rangeStance;
int initialStance;
Person(std::string name, int rangeStance, int initialStance){
name = name;
rangeStance = rangeStance;
initialStance = initialStance;
setName(getName());
setRangestance(getRangeStance());
setinitalStance(getRangeStance());
}
Person();
void setName(std::string name);
void setRangestance(int range);
void setinitalStance(int stance);
std::string getName();
int getRangeStance();
int getinitalStance();
double impact(int rangeStance, int initalStance);
};
class Leader: public Person {
public:
int popularity;
int totalcountryVotes;
Leader(std::string name, int rangeStance, int initialStance,int popularity, int totalcountryVotes)
:Person(name, rangeStance, initialStance), popularity(popularity), totalcountryVotes(totalcountryVotes){
popularity = popularity;
totalcountryVotes = totalcountryVotes;
setPopularity(getPopularity());
setTotalcountryVotes(getTotalcountryVotes());
}
Leader();
void setPopularity(int popularity);
void setTotalcountryVotes(int totalcountryVotes);
int getPopularity();
int getTotalcountryVotes();
};
The corresponding functions in the main cpp file.
Person::Person() {
}
void Person::setName(string Name)
{
name = Name;
}
string Person::getName() {
return name;
}
void Person::setRangestance(int Range)
{
rangeStance = Range;
}
int Person::getRangeStance() {
return rangeStance;
}
void Person::setinitalStance(int stance)
{
initialStance = stance;
}
int Person::getinitalStance() {
return initialStance;
}
Leader::Leader() {
}
void Leader::setPopularity(int popularity) {
popularity = popularity;
}
void Leader::setTotalcountryVotes(int totalcountryVotes) {
totalcountryVotes = totalcountryVotes;
}
int Leader::getPopularity() {
return popularity;
}
int Leader::getTotalcountryVotes() {
return totalcountryVotes;
}
Within main the needed funtions are called appropriately
int main(int argc, char* argv[]) {
Leader labourLeader("George Lopez",100,50,50, 75);//sets record for the labour party leader
cout << "--Party Leader--" << endl;
cout << labourLeader.getName() << endl;
return 0;
}
However when this snippet of code is compiled, no outcome is returned where it should be printing out "George Lopez". Im fairly "noob" with c++, am i using my contructor right or should I be delcaring it within my h file? Thankyou.
A couple of things wrong in this code
Person(std::string name, int rangeStance, int initialStance){
name = name;
rangeStance = rangeStance;
initialStance = initialStance;
setName(getName());
setRangestance(getRangeStance());
setinitalStance(getRangeStance());
}
Firstly it's not necessary to call setters and to do assignments, so lets drop those, leaving
Person(std::string name, int rangeStance, int initialStance){
name = name;
rangeStance = rangeStance;
initialStance = initialStance;
}
Now think about what name = name does. Does that look curious to you at all? It takes the parameter name and assigns it to the parameter name! The member variable also called name is completely unchanged. This situation where one name hides another similar name is called shadowing.
Person(std::string name, int rangeStance, int initialStance) {
name = name;
What's happening there is that it's just overwriting the parameter with itself, rather than copying it to the member variable. That's because the name lookup rules for unqualified names at that point prefer the parameter to the member variable. That means the member variable is being left at its constructed state, an empty string.
There are a few ways to fix this. The first is to simply name them differently so that there's no ambiguity, such as the common method of prefixing member variables with m_. That way, the statement becomes the more explicit:
m_name = name;
Another alternative is to be explicit about the one you're assigning to so that it's no longer unqualified:
this->name = name;
A third is to use initialiser lists where the rules are slightly different in that it uses the member variable outside the parentheses and does normal unqualified lookup within the parentheses:
Person(std::string name, int rangeStance, int initialStance)
: name(name)
, rangeStance(rangeStance)
, initialStance(initialStance)
// ^ ^
// | |
// | +- normal lookup, passed-in parameter.
// +--------------- member variable.
{
};
And there's no need to have all those other statements in the constructor, such as setName(getName()), since you've already set the name.

I dont understand what to do in the read() method

This is my c++ homework and i dont really get what they meant by setting the values in the method read().
Question: Create a base class called Athlete that contains 2 member variables for attributes common to all professional athletes: name and annual salary. It should also contain pure virtual method, read(). The method read() is called to read data from the user for setting the values of the attributes.
Here is my header file
#ifndef ATHLETE_H
#define ATHLETE_H
#include <string>
using namespace std;
class Athlete
{
public:
Athlete();
~Athlete();
void setName(string name);
string getName() const;
void setSalary(double salary);
double getSalary() const;
virtual void display() const;
virtual void read(string name, double salary) const;
private:
string name;
double salary;
};
#endif
And my cpp
#include "Athlete.h"
#include <iostream>
Athlete::Athlete() {}
Athlete::~Athlete() {}
string Athlete::getName() const { return this->name; }
void Athlete::setName(string name) {
this->name = name;
}
double Athlete::getSalary() const {
return this->salary;
}
void Athlete::setSalary(double salary) {
this->salary = salary;
}
void Athlete::read(string name, double salary) const {
Athlete* temp = new Athlete();
temp->setName(name);
temp->setSalary(salary);
}
void Athlete::display() const {
cout << "Name: " << this->getName() << endl;
cout << "Salary: " << this->getSalary() << endl;
}
I tried to use the setter methods in read but theres an error.
I think you misread the question. It says that the read() method should read the data from the user. Usually it means read from the standard input. Afterwards, the method should set the values of the attributes for this specific athlete. Meaning, that the entered values relate to this specific object. Not for something new and temporary.
Pulling everything together is may look like the following:
void Athlete::read()
{
string name;
double salary;
std::cout << "Please enter the athlete name:";
std::cin >> name;
std::cout << "Please enter the athlete salary:";
std::cin >> salary;
setName(name);
setSalary(salary);
}
The thing you've missed is that read is supposed to be a pure virtual function. This means that you should not actually implement it, instead you should declare it as:
virtual void read(string name, double salary) = 0;
This means that the Athlete class cannot actually be instantiated (it's called an absract class), instead it will be used as a base class and derived classes would be required to override the read method. If they don't override the method they will themselves be abstract and cannot be instantiated.
You are not required to implement the read method as a method of Athlete once you declared it as pure virtual. It only need to be implemented as a method in the derived class(es).
Also as the method in the derived class is supposed to modify the object the method cannot be const declared (as shown above).

C++: how to make getters and setters work with an empty constructor

First of all, I have only learned a little bit of Java before. It's been only a few days since I started getting friendly with C++ so please don't take this question so basic and please don't degrade my question.
I made a simple source code as follows:
#include <iostream>
using namespace std;
class Car {
public:
void setBrand(string name);
void setPrice(double price);
string getBrand();
double getPrice();
Car();
Car(string name);
Car(string name, double price);
private:
string name;
double price;
};
Car::Car() {
}
Car::Car(string name) {
name = name;
}
Car::Car(string name, double price) {
name = name;
price = price;
}
void Car::setBrand(string name) {
name = name;
}
void Car::setPrice(double price) {
price = price;
}
string Car::getBrand(void) {
return name;
}
double Car::getPrice(void) {
return price;
}
int main() {
Car car;
car.setBrand("Nissan");
car.setPrice(30000);
cout << "Brand: " << car.getBrand() << endl;
cout << "Price: " << car.getPrice() << endl;
return 0;
}
I wanted to make a code that creates an empty instance of a class called Car, set the field values later and print them out on the console.
The code did not make any errors during the compile, but the result I see was totally different from what I expected. It didn't show the brand name and the price was looking even weird, as follows.
Brand:
Price: 6.95322e-310
Somebody help me out! Thank you very much indeed in advance.
The problem you have is that you override the member names with function parameters. You can use this-> to make it explicit or name the member differently.
For example:
void Car::setBrand(string name) {
this->name = name;
}
Or:
void Car::setBrand(string new_name) {
name = new_name;
}
In your constructor and setters, you make no differentiation between the local parameter and the class member.
name = name;
Both the function parameter and the class member are called name. Currently the compiler is assigning the parameter value to itself, and not affecting the class member at all. This is because the function parameter is in a more immediate scope.
Possible solutions:
Specify this when referring to the class member: this->name = name;.
Rename the function parameter: name = _name;.
For the constructor, use initializer lists:
Car::Car(string name, double price)
: name(name)
, price(price)
{ }
There's too much wrong with your code to describe it in prose, so let me present a fixed implementation, and I leave it to you to spot the difference:
#include <string>
class Car
{
private:
static constexpr double kNoPrice = -1.0;
static constexpr const char* kNoName = "[no name]";
public:
// Main constructor: constructs a car with the given name and price.
Car(std::string name, double price)
: name_(std::move(name))
, price_(price)
{}
// Convenience constructors:
Car() : Car(kNoName, kNoPrice) {}
Car(std::string name) : Car(std::move(name), kNoPrice) {}
// Accessors:
const std::string& getBrand() const { return name_; }
void setBrand(std::string name) { name_ = std::move(name); }
double getPrice() const { return price_; }
void setPrice(double price) { price_ = price; }
private:
std::string name;
double price;
};
Some random notes, in no particular order:
Use correct names. It's std::string, not string, mate or buddy. Never ever be abusing namespace std.
Include headers for external names that you need.
Reading uninitialized values is undefined behaviour, so none of your constructors should leave fields uninitialized (like price_).
Give private members consistent names (e.g. foo_ in my example).
Accessors should be const-correct.
Convenience constructors should delegate to one single work-horse constructor.
Pick sensible defaults for initial values of defaulted fields and make them discoverable.
Use move semantics when taking ownership of dynamically managed data (strings, dynamic containers, etc.).