Implementing an Assignment Operator - c++

I am trying to write an overloaded operator= to assign copy the content of an object studentA to studentB. After compiling, it just crashes and as usual "stops working".
#include <cstdlib>
#include <iostream>
using namespace std;
class STUDENT {
public:
string ID;
string name;
unsigned int birthyear;
STUDENT(string x, string y, unsigned int z) {
ID = x;
name = y;
birthyear = z;
};
STUDENT operator = (STUDENT const &obj) {
STUDENT *res;
*res = obj;
return *res;
}
void print() {
cout << ID << " " << name << " " << birthyear;
}
};
int main() {
STUDENT studentA("1951049", "Nguyen Vinh Hao", 2001);
STUDENT studentB("1951050", "Nguyen Van Hao", 1999);
studentB = studentA;
studentB.print();
return 0;
}
I think there are some problems with the pointer *res, but I don't really know what they are. I hope you can help me.

Your operator= is implemented all wrong. Your res pointer does not point anywhere meaningful when you dereference and assign to it. That is why your code is crashing.
You need to actually copy the individual members yourself from obj to *this, similarly to how you are doing so in your constructor, eg:
#include <iostream>
using namespace std;
class STUDENT {
public:
string ID;
string name;
unsigned int birthyear;
STUDENT(string x, string y, unsigned int z) {
ID = x;
name = y;
birthyear = z;
}
STUDENT& operator=(STUDENT const &obj) {
ID = obj.ID;
name = obj.name;
birthyear = obj.birthyear;
return *this;
}
void print() const {
cout << ID << " " << name << " " << birthyear;
}
};
int main() {
STUDENT studentA("1951049", "Nguyen Vinh Hao", 2001);
STUDENT studentB("1951050", "Nguyen Van Hao", 1999);
studentB = studentA;
studentB.print();
return 0;
}
That being said, you are using types that the compiler already knows how to copy, so you should let the compiler generate a suitable copy assignment operator for you, eg:
#include <iostream>
using namespace std;
class STUDENT {
public:
string ID;
string name;
unsigned int birthyear;
STUDENT(string x, string y, unsigned int z) {
ID = x;
name = y;
birthyear = z;
}
//STUDENT& operator=(STUDENT const &) = default;
void print() const {
cout << ID << " " << name << " " << birthyear;
}
};
int main() {
STUDENT studentA("1951049", "Nguyen Vinh Hao", 2001);
STUDENT studentB("1951050", "Nguyen Van Hao", 1999);
studentB = studentA; // <-- automatically does the "right thing" for you!
studentB.print();
return 0;
}

In your case, you do not need to implement your own copy assignment operator=, because both std::string and (unsigned) int types have their own built-in copy operator. In other words, they know how to copy safely their own resources.
As #Remy Lebeau already suggested the best option is to use the default:
class_name & class_name :: operator= ( const class_name & ) = default;
Even if you used Containers from the standard template library containers as your member variables,
class STUDENT {
public:
string ID;
string name;
unsigned int birthyear;
vector v; /*for example std::vector*/
};
again, you shouldn't need to worry about copying, moving, or managing the memory of your class. They handle their resources!
The container manages the storage space that is allocated for its
elements and provides member functions to access them, either directly
or through iterators (objects with properties similar to pointers).
You should start worrying about copy semantics when you introduce pointers in your classes, given that those pointers are responsible for managing the resources where they point to!
class STUDENT {
public:
string ID;
string name;
unsigned int birthyear;
float * p; /* this can introduce bug if not handled properly */
};
You can read more on previous questions:
here, and here

Here STUDENT *res; is currently not pointing anywhere and so you can't assign it values.
You don't need STUDENT *res; here
You can just return obj;
or if you want obj to be assigned to another pointer and then return it, you can do
STUDENT operator=(STUDENT const &obj)
{
const STUDENT *res;
res = &obj;
//here the pointer res points to the same address as the obj, thus they are pointing to same object.
return *res;
}

Related

How do you make an object as a parameter for a function c++

I am trying to make an object as a parameter for my add() function in this code:
class EnterInfo
{
protected:
string name;
int age;
string birthmonth;
public:
EnterInfo()
{
}
EnterInfo(string n, int a, string b)
{
name = n;
age = a;
birthmonth = b;
}
};
class CourseInfo
{
protected:
string title;
public:
CourseInfo()
{
}
CourseInfo(string t)
{
title = t;
}
void add()
{
}
};
int main()
{
EnterInfo s1;
CourseInfo c1;
s1 = EnterInfo("Sand", 20, "Jan");
c1 = CourseInfo(" x Records");
}
I want the add() function to gather all the data from the object "s1" and compact it into an array that I can access later. I could add, remove, or edit the data moving forward or even maybe make a new object "c2" which contains "y records" with the same s1 values ("sand", 20, "Jan"), however, I have no idea how to implement this in code.
c1.add(s1); // assume s1 (EnterInfo): Sand 20 Jan
c1.add(s2); // assume s2 (EnterInfo): Paul 23 Aug
this is the code I want to work with. I don't know how to make it work though. The end game would be this
c1.print();
output:
x records
Sand 20 Jan
Paul 23 Aug
create a vector of EnterInfo objects and put it in CourceInfo class. The code below does what you need:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class EnterInfo
{
public:
string name;
int age;
string birthmonth;
EnterInfo()
{
name = "";
age = 0;
birthmonth = "";
}
EnterInfo(string n, int a, string b)
{
name = n;
age = a;
birthmonth = b;
}
};
class CourseInfo
{
protected:
string title;
vector<EnterInfo> info_vec;
public:
CourseInfo()
{
title = "";
}
CourseInfo(string t)
{
title = t;
}
void add(const EnterInfo enterInfo)
{
this->info_vec.push_back(enterInfo);
}
void print() {
cout << this->title << endl;
for (const auto& it : this->info_vec) {
cout << it.name << " " << it.age << " " << it.birthmonth << endl;
}
}
};
int main()
{
EnterInfo s1("Sand", 20, "Jan");
EnterInfo s2("Arash", 21, "Feb");
CourseInfo c1(" x Records");
c1.add(s1);
c1.add(s2);
c1.print();
}
Side notes:
1- It's better to assign default values to the members of your class in the constructor.
2- I changed the access level of your EnterIndo members into public in order to use them in add function but the standard way is to set them to private and create getters and setters for them.
Research about std::vector and get/setters if you are not familiar with them.

EXC_BAD_ACCESS when adding object to array

I am new programming and C++. I am trying to create a Roster of Person objects using an array and then printing the attributes of the People in the Roster.
When I attempt to add a Person to the personArray, I am getting an
Exception = EXC_BAD_ACCESS (code=1, address=0x0).
I think this has to do with the scope of my personArray but I can't seem to figure it out.
Here is the code I am using:
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
class Person
{
public:
Person(string name, int age);
string getName() {
return name;
}
void setName(string n) {
name = n;
}
int getAge() {
return age;
}
void setAge(int a) {
age = a;
}
private:
string name;
int age;
};
class Roster {
public:
void addToPersonArray(string name, string age) {
Person person(name, stoi(age));
personArray[personCount] = &person;
}
void printPersonArray() {
for (int i = 0; i < 5; i++)
cout << personArray[i]->getName() << '\t' << personArray[i]->getAge() << '\n';
}
private:
Person *personArray[5];
int personCount = 0;
};
int main() {
const string studentData[] = {"Dan,45", "Mark,33", "Mary,22",
"April,17", "Jill,22"};
Roster roster;
stringstream ss(studentData[0]); // just testing the first entry
vector<string> result;
while (ss.good()) {
string substr;
getline(ss, substr, ',');
result.push_back(substr);
}
roster.printPersonArray();
}
The problem is here:
void addToPersonArray(string name, string age) {
Person person(name, stoi(age));
personArray[personCount] = &person;
}
person is a local variable in the member function addToPersonArray(), which will be destroyed after the function scope.
Hence, storing the address of a local variable(and trying to accessing it latter in printPersonArray()) will give you, nothing but an undefined behavior.
You are lucky that your programme got the exception.
One more thing to note that, you are not actually using your roster to test the program. Instead, all you do is parsing and saving to result vector. You can add this after the while loop, to make it actually work.
if (result.size() == 2) {
roster.addToPersonArray(result[0], result[1]);
}
Suggestion: Since you have a fixed array size, you probably wanna do it with std::array<Person, 5> or with std::vector<Person> by reserving the memory for 5 Person in the c'tor of Roster.
See a sample output: https://wandbox.org/permlink/tAGqqnhCfwz1wPrH
#include <iostream>
#include <sstream>
#include <vector>
#include <array>
class Person {
public:
Person(const std::string& name, int age): name(name), age(age) {}
std::string getName()const { return name; }
void setName(const std::string& n){ name = n; }
int getAge()const { return age; }
void setAge(int a) { age = a; }
private:
std::string name;
int age;
};
class Roster {
public:
Roster() { personArray.reserve(5); } // reserve some memory
void addToPersonArray(const std::string& name, const std::string& age) {
personArray.emplace_back(name, stoi(age));
}
void printPersonArray() {
// use range loop, which will assure, access what you have.
for (const Person& person: personArray)
std::cout << person.getName() << '\t' << person.getAge() << '\n';
}
private:
std::vector<Person> personArray;
//int personCount = 0; --------------> no need anymore
};
int main() {
std::array<std::string,5> studentData{ "Dan,45", "Mark,33", "Mary,22", "April,17", "Jill,22" };
Roster roster;
for(const std::string& str: studentData)
{
std::stringstream ss(str);
std::vector<std::string> result;
while (ss.good()) {
std::string substr;
std::getline(ss, substr, ',');
result.emplace_back(substr);
}
if (result.size() == 2) {
roster.addToPersonArray(result
[0], result[1]);
}
}
roster.printPersonArray();
return 0;
}
Output:
Dan 45
Mark 33
Mary 22
April 17
Jill 22
In addition to storing pointers to local variables in your addToPersonArray() function, your main() function does not add any entries to the personArray.
Instead, main creates a Roster object, and after some code that doesn't affect anything with Roster, you go straight into calling roster.printPersonArray, which does this:
void printPersonArray()
{
for (int i = 0; i < 5; i++) // <-- This will loop for all 5 entries
cout << personArray[i]->getName() << '\t' << personArray[i]->getAge() << '\n';
}
Since personArray was never initialized to contain valid pointers to Person objects, that loop will cause undefined behavior.
The issue is that you have a personCount member variable, but fail to make use of it to control how many valid entries there are in the array. The loop should have been written as below:
void printPersonArray()
{
for (int i = 0; i < personCount; i++)
cout << personArray[i]->getName() << '\t' << personArray[i]->getAge() << '\n';
}
In addition to the storage of pointers to local variables, your Roster::addToPersonArray() doesn't check if personCount is greater than 4, thus this is another place in your code where you failed to use personCount to control how many Person objects are being referenced.

How can I assign class object into a class

I need to creat an object Pokemon in the main()
that assign it into the class PokemonWorld, and let the PokemonWolrd to decide which PokemonStation is this Pokemon need to go
I tired get the data separatly (get name and hp) and get together(get a Pokemon class)
but both fail
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;
class Pokemon {
public:
Pokemon() {};
Pokemon(char x[], int n) {
strncpy_s(name, x, 10);
hp = n;
};
private:
char name[10];
int hp;
};
class PokemonStation {
private:
Pokemon **list= new Pokemon*[1000];
public:
PokemonStation() {};
PokemonStation(int x) {
id = x;
};
int id;
void assigntoList(int i,Pokemon x)
{
if (i > 0)
i--;
list[i] = new Pokemon(x);
cout << "creat" << list[i];
};
};
class PokemonWorld {
private:
char name[10];
public:
PokemonStation s1;
PokemonStation s2;
PokemonWorld() {};
PokemonWorld(char x[], int y=1, int z=2) {
strncpy_s(name, x, 10);
PokemonStation s1(y);
PokemonStation s2(z);
};
const char* const getName() const{
return name;
};
void assigntoStation(int i,Pokemon x) {
if (i == 0 || i % 2 == 0)
s1.assigntoList(i, x);
else
s2.assigntoList(i, x);
};
};
void main() {
int number,hp,i;
char name[10];
cout << "What is the World Name ?" <<endl;
cin >> name;
PokemonWorld world(name);
cout << "Please input the number of Pokemon in the " << world.getName() <<" world:" << endl;
cin >> number;
Pokemon **mon = new Pokemon*[number];
cout << "Please input the characteristics of all Pokemon: Name HP" << endl;
for (i = 0;i < number;i++)
{
cin >> name >> hp;
mon[i] = new Pokemon(name, hp);
world.assigntoStation(i,*(mon[i]));
}
for (i = 0;i < number;i++)
cout << "world is " << world.getName() << endl;
system("pause");
};
In C++, you should use std::vectors for dynamic lists of things and std::strings for text. If you know Java, these are like ArrayList and String. (To use these, make sure you #include <vector> and <string>.)
For instance, your Pokemon class, rewritten with name as a string:
class Pokemon {
public:
Pokemon() {}
Pokemon(string name, int hp):name(name), hp(hp) { // construct the fields directly
}
private:
string name;
int hp;
};
And your PokemonStation class, rewritten using a vector for the list of Pokemon:
class PokemonStation {
private:
vector<Pokemon> list;
public:
PokemonStation() {}
PokemonStation(int x):id(x) {}
int id;
void assignToList(Pokemon x)
{
list.push_back(x); // add x to the list
}
};
If you want to print a Pokemon with cout <<, then you'll have to overload the << operator to define what gets printed. Add this into your class:
class Pokemon {
public:
...
friend ostream& operator<<(ostream& out, const Pokemon& p) {
out << p.name; // just print the name
return out;
}
...
};
Just make sure that you're couting a Pokemon, not a pointer-to-Pokemon (Pokemon*), and you won't get an address.

how do i create an array where each element in the array, of size 10, is pointing to an object, in C++?

for example lets have a class or struct name Employee with two constructors, a default constructor and a constructor with parameters two strings and an int. why doesn't the following code work?
Employee *employees = (employee*) malloc(sizeof(Employee)*10);
let's say we have an array, size 10, of type string for first name, last name, and one of type int for salary. how to initialize the data members of each object class using the constructor with the parameters?
for (int i = 0; i < 10; i++)
{
employees[i] = employee(firstname[i], lastname[i], salary[i]);
}
I've been trying to do this for a few days now but wasn't successful. Also, can anyone tell how to do this using c++'s new and delete operator? and also is there a way this can be done using vectors?
Thank you
header file
class employee{
std::string firstname;
std::string lastname;
int salary;
public:
employee(std::string, std::string , int);
employee();
void setFirst(std::string);
void setLast(std::string);
void setSalary(int);
std::string getFirst();
std::string getLast();
int getSalary();
};
employee::employee(std::string x, std::string y, int z)
{
setFirst(x);
setLast(y);
setSalary(z);
}
void employee::setFirst(std::string x)
{
firstname = x;
}
void employee::setLast(std::string y)
{
lastname = y;
}
void employee::setSalary(int z)
{
salary = z > 0 ? z : 0;
}
std::string employee::getFirst()
{
return firstname;
}
std::string employee::getLast()
{
return lastname;
}
int employee::getSalary()
{
return salary;
}
.cpp file
#define MAX 20
using namespace std;
int main(int argc, char* argv[])
{
int n = 1;
cout << "number of employees: ";
cin >> n;
string firstname[MAX];
string lastname[MAX];
double salary[MAX];
float raise[MAX];
for (int i = 0; i < n; i++)
{
cout << "Employee " << i + 1 <<endl;
cout << "-----------\n";
cout << "First Name: ";
cin >> firstname[i];
cout << "Last Name: ";
cin >> lastname[i];
cout << "Monthly Salary: ";
cin >> salary[i];
salary[i] *= 12;
cout <<"Yearly percentage raise (e.g 10% or 0%): ";
scanf("%f%%", &raise[i]);
salary[i] *= (((raise[i])/100.00) + 1);
puts("\n");
}
employee *employees = (employee*) malloc(sizeof(employee)*10);
for (int i = 0; i < n; i++)
{
employees[i] = employee(firstname[i], lastname[i], salary[i]);
}
cout << "TESING USING GET FUNCTIONS" << endl;
cout << "---------------------------\n\n";
for (int i = 0; i < n; i++)
{
cout << "Employee " <<i +1<< endl;
cout <<"-----------\n";
printf("First Name: %s", employees[i].getFirst().c_str());
printf("\nLast Name: %s",employees[i].getLast().c_str());
printf("\nYearly Salary: %d\n\n", employees[i].getSalary());
}
}
If you have an array of Employee instances and Employee is not POD (http://en.wikipedia.org/wiki/POD) you need to allocate memory from the stack using the operator new:
Employee* employees = new Employee[10];
And for having this working:
for (int i = 0; i < 10; i++)
{
employees[i] = Employee(firstname[i], lastname[i], age[i]);
}
you need to implement the operator= in your Employee class:
Employee& operator=(const Employee& src)
{
_firstname = src._firstname;
_lastname = src._lastname;
_age = src._age;
return *this;
}
If this looks like your Employee class:
class Employee
{
std::string first_name;
std::string last_name;
// other members and functions ...
};
Then using malloc() to create 10 of these is a complete and utter failure. The reason why is that yes, you allocated memory using malloc(), but that's all you did. You didn't construct 10 Employee objects. Those std::string members need to be constructed, not merely have memory allocated. So with that call to malloc() you have 10 fake Employees that were "created", and as soon as you attempt to do anything with one of them, then boom goes your program.
Do research on POD and non-POD types. You cannot treat non-POD types (as the class above is non-POD) as you would a POD type. For a non-POD type, the instance must be "officially" constructed, (the constructor must be invoked).
On the other hand, malloc() knows nothing concerning C++ and what is required to create an object correctly via construction. All malloc (and calloc, and realloc) knows is to allocate bytes and return a pointer to the allocated space.
Use a vector instead, it's resizable, it's easier to manage, and as Grady stated in his comment, it's also generally not good practice to use malloc in C++ code (although it's possible). Maybe do something that looks like this:
#include <vector>
...
int size = 10;
std::vector<Employee *> employees;
for(int i = 0; i < size; i++)
{
//as far as pulling in your data, that depends on where it's coming from
Employee *temp = new Employee(...);
employees.push_back(temp);
}
I'm rusty on my C++ but this should work.
Try this way:
#include <iostream>
class Employee
{
std::string m_firstname;
std::string m_lastname;
int m_age;
public:
Employee()
{
m_firstname=m_lastname="";
m_age=0;
}
void setFirstName(std::string firstname)
{
m_firstname=firstname;
}
void setLastName(std::string lastname)
{
m_lastname=lastname;
}
void setAge(int age)
{
m_age=age;
}
void displayEmp()
{
std::cout<<m_firstname;
std::cout<<m_lastname;
std::cout<<m_age;
}
};
int main()
{
std::string fname;
std::string lname;
int age;
Employee *employee = new Employee[10];
Employee *employeeptr=employee;
for(int i=0;i<10;i++)
{
std::cin>>fname;
std::cin>>lname;
std::cin>>age;
employeeptr->setFirstName(fname);
employeeptr->setLastName(lname);
employeeptr->setAge(age);
employeeptr++;
}
employeeptr=employee;
for(int i=0;i<10;i++)
{
employeeptr->displayEmp();
employeeptr++;
}
delete []employee;
return 0;
}

C++ preprocessor and operator + overloading

I want to use the C++ preprocessor to be able to write the following in any C++ block.
class Student {
private:
int age;
int grade;
int courses;
}
int main(){
CREATE_STUDENT 15+62+2 ;
}
The previous code will create a Student with these 3 members.
I want to use + operator overloading.
Any idea of how to do it?
I want EXACTLY the syntax I mentioned above.
Why not just use a constructor:
class Student {
private:
int age;
int grade;
int courses;
public:
Student(int a, int g, int c)
{
age = a;
grade = g;
courses = c;
}
}
int main(){
Student s(15,62,2);
}
Well, I completely fail to understand why you would want to do such a thing. But it is possible, sorta.
You'll need to make it a bit more complex than that to be able to use more than one such "construct" in the same block though.
#include <iostream>
#define GRADE_STUDENT Student student = (Student)
class Student {
public:
Student(int a): age(a), grade(-1), courses(-1), setup(0) {};
Student& operator+(int p)
{
switch(setup) {
case 0: grade = p; break;
case 1: courses = p; break;
default: /* die */ char *p=0; *p=0;
}
setup++;
return *this;
};
void print()
{
std::cout << age << ", " << grade << ", " << courses << std::endl;
};
private:
int age;
int grade;
int courses;
int setup;
};
int main()
{
{
GRADE_STUDENT 15+62+2 ;
student.print();
}
{
GRADE_STUDENT 15+62 ;
student.print();
}
{
GRADE_STUDENT 15+62+2+3 ; // crash
}
return 42;
}
You should template your class instead of working with the preprocessor.