Pointers practice improvement - c++

Hello! I wanted to learn more about pointers, so i wrote the following:
struct Data {
int day, month, year;
double hour;
};
class Movie {
protected:
string name;
Data data;
int requiredAge;
int freeSeats;
public:
Movie(){ }
Movie(string moviename, Data dat, int age, int seats) {
name = moviename;
data = dat;
requiredAge = age;
freeSeats = seats;
}
int getFreeSeats() { return freeSeats; }
~Movie(){ }
};
And then i initialize it into the main:
int _tmain(int argc, _TCHAR* argv[])
{
Data d; d.day = 12; d.hour = 16; d.month = 04; d.year = 2016;
Movie movie("Movie Name", d, 16, 35 );
system("pause");
return 1;
}
Why should i use pointers here and where?

The only place where it could make sense to use pointers here is for passing the struct to the constructor:
Movie(string moviename, Data* dat, int age, int seats) {
name = moviename;
data = *dat;
requiredAge = age;
freeSeats = seats;
}
This would avoid creating a complete copy of the struct when the constructor is called. Just passing the pointer is a bit cheaper.
While in C, this would be the only option, in C++ there is a better way: Use references:
Movie( const string& moviename, const Data& dat, int age, int seats) {
name = moviename;
data = dat;
requiredAge = age;
freeSeats = seats;
}
By the way, for the intarguments it is not useful to pass them by reference bacause they are not larger (in terms of bytes) than a pointer.
An even better version would use an initialization list:
Movie( const string& moviename, const Data& dat, int age, int seats)
: name(moviename), data(dat), requiredAge(age), freeSeats(seats) {}
(Thanks for the hint, #NathanOliver)

Related

the code doesn't display and doesn't run either

Below is a program that has class definitions for Item, Customer and Sales. The main simply creates object object of each class and test its member functions. Modify the main program such that it provides a menu driven interface where user can create objects of Item, Customer and a complete a sales transaction with the sales object.The program should also have an option for display the records of items,customers and sales.To make your program more useful,include file handling such that when objects are created for Items,Customers and Transaction,the user will be prompted to save the recordon the file or not.
here's the code it's not displaying anything pleasee help i'm running it by Dev c++
#include <conio.h>
#include <iostream>
#include <string.h>
using namespace std;
class Item {
int itemCode;
private:
double price;
double discount;
protected:
int qtyOnStock;
char *name;
public:
Item() {
itemCode = 0;
strcpy(name, "UNKNOWN");
price = 0;
discount = 0;
qtyOnStock = 100;
}
void setItemCode(int c) { itemCode = c; }
int getItemCode() { return itemCode; }
double getPrice() { return price; }
void setPrice(double p) { price = p; }
void setDiscount(double d) { discount = d; }
double getDiscount(double d) { return ((d < 20 ? d : 20) / 100 * price); }
void setName(char *n) { name = n; }
char *getName() { return name; }
void setQtyOnStock(int q) { qtyOnStock = q; }
int getQtyOnStock() { return qtyOnStock; }
};
class Customer {
private:
int id;
char *name;
char *contactNo;
int type;
public:
Customer() {
id = 0;
strcpy(contactNo, "No Num");
strcpy(name, "No Name");
type = 0;
}
void setId(int newId) { id = newId; }
int getId() { return id; }
void setName(char *n) { strcpy(name, n); }
char *getName() { return name; }
void setContactNo(char *c) { strcpy(contactNo, c); }
char *getContactNo() { return name; }
};
class Sales {
private:
Item item;
Customer cust;
char *date;
int qtySold;
public:
Sales() { date = "mm-dd-yyyy"; }
void setItem(Item newItem) { item = newItem; }
Item getItem() { return item; }
void setCustomer(Customer newCust) { cust = newCust; }
Customer getCustomer() { return cust; }
void setDate(char *newDate) { strcpy(date, newDate); }
char *getDate() { return date; }
void setQtySold(int newQty) { qtySold = newQty; }
int getQtySold() { return qtySold; }
};
int main() {
Item item1;
Customer cust1;
Sales sales1;
item1.setItemCode(143);
item1.setName("Ballpen");
item1.setPrice(12.5);
item1.setQtyOnStock(250);
cust1.setId(123);
cust1.setName("Juan dela Cruz");
sales1.setItem(item1);
sales1.setCustomer(cust1);
sales1.setDate("10-27-2018");
sales1.setQtySold(98);
item1.setQtyOnStock(item1.getQtyOnStock() - sales1.getQtySold());
system("cls");
cout << sales1.getItem().getName() << endl << item1.getQtyOnStock();
getch();
return 0;
}
The main and biggest proble is that you do C-style string handling with char* and even that in a wrong way.
If you would enable all warning in your compiler, it would already tell you the problems. My VS2019 gives 15 errors, 1 warning and 7 messages, when I try to compile your code. Please see:
So, the main problem is that you are using char* that are not initialzed, meaning they point to somehwere, and that you do not allocate memory to store your strings.
So all your strcpy functions will fail and probably crash your system. Also the assignments to a char* will fail in most cases.
You will overwrite some random memory.
All this can be immediately fixed, without big problems, if you would use std::string instead of char*. Because char* are that error prone, C++ introduced the std::string, so, please use it.
Sometimes you have C++ teachers that want you to use char*. Those teachers should be fired. But if you really need to use char*. Then you must allocate memory, before coping data.
Let us assume that you have a string "myName" and you want to copy that.
char* name{};
name = new char[strlen(myName)+1]; // +1 for the trailing '\0'
strcpy(name, myName);
// ...
// ...
// Do stuff
// ...
// ...
delete [] name; // Release memory at the end
But as said. Simply use std::string
Your program as is, cannot work. You need a major refactoring.
In your Item class:
protected:
int qtyOnStock;
char *name;
public:
Item() {
itemCode = 0;
strcpy(name, "UNKNOWN");
price = 0;
discount = 0;
qtyOnStock = 100;
}
name is an unitialized char pointer so copying to it will result in UB.
change char* name to std::string name, replace strcpy(...) name = "UNKNOWN".
Normally though you initialize member variables like this:
Item()
: itemCode(0), itemCode(0), name("UNKNOWN"), price(0), discount(0), qtyOnStrock(100)
{}
a newer compiler lets you initialize in other ways like when declared e.g.:
protected:
int qtyOnStock{100};
std::string name{"UNKNOWN"};
...

Implementing an Assignment Operator

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;
}

Passing c-strings to class function (assigning it to class member)

I have a class called Person:
class Person {
private:
char name[50];
unsigned int number;
public:
void set_data(const char *newname, unsigned int number) {
*name= *newname; //THE MAIN PROBLEM I WANT TO SOLVE
this->number = number;
}
char* get_name() {
return this->name;
}
unsigned int get_number() {
return this->number;
}
};
I have arrays:
const char *names[] = {"Mark", "Pavel", "Bill", "Jasur", "Jeff"};
int phones[] = { 1234567890, 9876543210, 123321456654, 1998946848479, 1234554321 };
I'm using for loop to set Person members:
Person p;
for (int i = 0; i < 5; i++) {
p.set_data(names[i], phones[i]);
cout << p.get_name() << endl;
}
When I run this, I'm getting wrong output.
Using * the way you are, you are only copying the 1st char into name. You are also not null-terminating name either, which is required by the overloaded operator<< that takes a char* as input.
To copy the entire string, you need to use std::strcpy() (or better, std::strncpy()) instead, eg:
#include <cstring>
void set_data(const char *newname, unsigned int newnumber) {
//std::strcpy(name, newname);
std::strncpy(name, newname, 49);
name[49] = '\0';
number = newnumber;
}
However, since this is C++ and not C, you really should be using std::string instead:
#include <string>
class Person {
private:
std::string name;
unsigned int number;
public:
void set_data(const std::string &newname, unsigned int newnumber) {
name = newname;
number = newnumber;
}
std::string get_name() const {
return name;
}
/* or:
const std::string& get_name() const {
return name;
}
*/
unsigned int get_number() const {
return number;
}
};

default value for a pointer parameter

I'm trying to create a class for employees, and have a problem with its constructor.
My class looks like that (please note the name parameter which is char* type):
class Employee {
int id;
char * name ;
float salary;
int hours;
int extra;
public:
//constructor
Employee(int, char *, float, int, int);
//Getters and Setters:
void setId(int a) { id = a; }
int getId() { return id; }
void setName(char * c) { name = c; }
char * getName() { return name; }
void setSalary(float f) { salary = f; }
float getSalary() { return salary; }
void setHours(int h) { hours = h; }
int getHours() { return hours; }
void setExtra(int e) { extra = e; }
int getExtra() { return extra; }
};
I built a constructor and I want it to have default parameters, and I don't know how to deal with the name parameter to have a default of let's say "unknown".
This is the constructor:
Employee::Employee(int i = 123456789, char * na, float sal = 30, int ho = 45, int ex = 10)
{
id = i;
name = na;
salary = sal;
hours = ho;
extra = ex;
}
You can use a character array, and initialise the array to point to the first element of the array. You can store the array for example as a static member:
// in the class definition
inline static char default_name[] = "unknown";
// in argument list of the constructor
... char * na = default_name, ...
Although, you may want to consider whether it makes sense for name to be pointer to non-const. Perhaps a pointer to const would suffice. In such case, you could initialise it to point to the literal "unknown" directly.
A cleaner version
class Employee {
int id = 0;
char *name = nullptr;
float salary = 0.0;
int hours = 0;
int extra = 0;
And you don't need to have constructors, this depends on the case, but you get the idea that by initializing the variables on the definition you reduce the inconsistency of having multiples constructors for example

C++ Inheritance of functions, passing in arguments

Base class : Employee
Derived class : Regular
Employee.cpp
void Employee::setValue(string id, string name, double s, int n)
{
empID = id;
empName = name;
salary = s;
}
Regular.cpp
void Regular::setValue(string id, string name, double s, int n)
{
annualLeave = n;
}
Employee::setValue() only stores the first 3 arguments passed in, but not int n, too.
I'm supposed to inherit that setValue() in Regular::setValue() and then just pass in the arguments, but this time store int n to annualLeave.
How do I do that?
Or, is there a way for me to set int n in the base class for the child class?
You can call the base class's implementation:
void Regular::setValue(string id, string name, double s, int n) {
annualLeave = n;
return Employee::setValue(std::move(id), std::move(name), s);
}
Otherwise, make base class polymorphic:
struct Employee {
void setValue(string id, string name, double s, int n) {
empID = std::move(id);
empName = std::move(name);
salary = s;
setLeave(n);
}
virtual ~Employee() {}
protected:
virtual void setLeave(int) = 0;
string empID;
string empName;
double salary;
};
struct Regular: Employee {
private:
void setLeave(int n) override { annualLeave = n; }
int annualLeave;
};
If necessary to keep a single-signature setValue function, it is possible to do it like that:
-
Includes:
#include <any>
#include <map>
#include <string>
-
Employee.h:
class CEmployee
{
protected:
virtual void setValue(std::map<std::string, std::any> &info);
int m_empID = 0;
std::string m_empName = {'\0'};
int m_salary = 0;
}
Employee.cpp:
void CEmployee::setValue(std::map<std::string, std::any> &info)
{
std::any item;
item = info["empID"];
if (item.has_value())
m_empID = std::any_cast<int>(item); // int
item = info["empName"];
if (item.has_value())
m_empName = std::any_cast<std::string>(item); // std::string
item = info["salary"];
if (item.has_value())
m_salary = std::any_cast<int>(item); // int
}
-
Regular.h:
class CRegular : public CEmployee
{
public:
void setValue(std::map<std::string, std::any> &info) override;
protected:
std::string m_annualLeave = {'\0'};
}
Regular.cpp:
void CRegular::setValue(std::map<std::string, std::any> &info)
{
std::any item;
CEmployee::setValue(info);
item = info["annualLeave"];
if (item.has_value())
m_annualLeave = std::any_cast<std::string>(item); // std::string
}
-
& call it like that:
void MyClass::HardcodedExample()
{
CRegular regular_employee;
std::map<std::string, std::any> info, update;
info["empID"] = { 100 };
info["empName"] = { std::string("Trump") };
info["salary"] = { 1000000 };
info["annualLeave"] = { std::string("29 Jul 2018") };
regular_employee.setValue(info); // Set all info
// Or:
update["annualLeave"] = { std::string("29 Dec 2018") };
regular_employee.setValue(update); // Update just "annualLeave"
// Or:
update["salary"] = { 1200000 };
update["annualLeave"] = { std::string("04 Jul 2018") };
regular_employee.setValue(update); // Update "salary" & "annualLeave"
}
-
Otherwise, setValue with 3 parameters to base-class, & with 4 parameters to the derived-class (that calls to the base-class with the 3 parameters and sets by itself the 4th one) - similar to what #RemyLebeauis offers - is a better solution.
-
& better to use #define / enum keys instead of string-keys (& change the key-type of the map accordingly), but this is a different issue.