C++ class destructor not working properly - c++

#include<iostream>
#include<cstring>
using namespace std;
class Animal
{
protected:
int age;
char* name;
public:
Animal()
{
name=new char[1];
age = 0;
strcpy(name," ");
}
Animal(int _age, char* _name)
{
age=_age;
name = new char[strlen(_name)+1];
strcpy(name, _name);
}
~Animal()
{
delete[] name;
}
friend istream& operator >>(istream& in, Animal& a);
friend ostream& operator <<(ostream& out, const Animal& a);
};
istream& operator >>(istream& in, Animal& a)
{
in>>a.name>>a.age;
return in;
}
ostream& operator <<(ostream& out, const Animal& a)
{
out<<a.name<<a.age;
return out;
}
int main()
{
Animal a;
cin>>a;
cout<<a;
return 0;
}
This piece of code gives me the opportunity to enter a, then prints it and then the screen freezes and stops working. If I delete the destructor, it works properly. Why is this happening? And is it because of the destructor really?

You allocate a C-string having the size 1 and copy the C-string " " having the size 2 to it. Also you read an unknown amount of characters to the name in 'istream& operator >>(istream& in, Animal& a)`. Both will corrupt the memory the name is pointing to and both can be easily fix by using std::string:
class Animal
{
protected:
int age;
std::string name;
public:
Animal()
: age(0)
{}
Animal(int age_, std::string name_)
: age(age_), name(name_)
{}
};
This avoids writing a destructor and copy-constructor and assignment operator, which are missing in your code (See: Rule of three).

If you really don't want to use std::string, your best bet is something in the line of (live at coliru):
#include<iostream>
#include<cstring>
using namespace std;
class Animal {
private:
// copy a string
inline static char* dstr(const char* string) {
if( !string ) return NULL;
size_t l = strlen(string);
if( !l ) return NULL;
return strcpy(new char[++l], string);
}
protected:
int age;
char* name;
public:
// initialize an "empty" Animal
Animal() : age(0), name(NULL) {}
// initialize an animal by age and name
Animal(int _age, const char* _name): age(_age), name(dstr(_name)) {}
// initialize an animal from another animal:
// copy the name string
Animal(const Animal& _a): age(_a.age), name(dstr(_a.name)) {}
// assign an animal from another animal:
// first delete the string you have, then copy the string
Animal& operator=(const Animal& _a) {
// for exception-safety, save the old "name" pointer,
// then try to allocate a new one; if it throws, nothing happens
// to *this...
char* oldname = name;
name = dstr(_a.name);
age = _a.age;
delete[] oldname;
return *this;
}
// if C++11
// we have something called "move" constructor and assignment
// these are used, for instance, in "operator>>" below
// and they assume that _a will soon be deleted
Animal(Animal&& _a): Animal() {
swap(age, _a.age);
swap(name, _a.name);
}
Animal& operator=(Animal&& _a) {
swap(age, _a.age);
swap(name, _a.name);
return *this;
}
~Animal() { delete[] name; }
friend ostream& operator <<(ostream& out, const Animal& a);
};
istream& operator >>(istream& in, Animal& a) {
const size_t MAX_ANIMAL_NAME = 2048;
int age;
char n[MAX_ANIMAL_NAME+1];
if( in.getline(n, MAX_ANIMAL_NAME) >> age )
a = Animal(age, n);
return in;
}
ostream& operator <<(ostream& out, const Animal& a) {
return out<<a.name<<endl<<a.age<<endl;
}
int main() {
Animal a { 23, "bobo" };
cout<<a;
cin>>a;
cout<<a;
}
This does not leak memory, does not have undefined behaviours, and does not have buffer overruns.
You can also segregate the "need to manage memory" to a separate class:
#include<iostream>
#include<cstring>
using namespace std;
class AnimalName {
private:
char *n;
inline static char* dstr(const char* string) {
if( !string ) return NULL;
size_t l = strlen(string);
if( !l ) return NULL;
return strcpy(new char[++l], string);
}
public:
AnimalName() : AnimalName(NULL) {}
AnimalName(const char *_n) : n(dstr(_n)) {}
AnimalName(const AnimalName& _n) : n(dstr(_n.n)) {}
// see exception-safety issue above
AnimalName& operator=(const AnimalName& _n) { char *on = n; n = dstr(_n.n); delete[] on; return *this; }
AnimalName(AnimalName&& _n) : AnimalName() { swap(n, _n.n); }
AnimalName& operator=(AnimalName&& _n) { swap(n, _n.n); return *this; }
~AnimalName() { delete[] n; }
operator const char*() const { return n; }
friend istream& operator>>(istream& i, AnimalName& n) {
const size_t MAX_ANIMAL_NAME = 2048;
char name[MAX_ANIMAL_NAME+1];
if( i.getline(name, MAX_ANIMAL_NAME) )
n = name;
return i;
}
};
class Animal {
protected:
int age;
AnimalName name;
public:
// initialize an "empty" Animal
Animal() : age(0) {}
// initialize an animal by age and name
Animal(int _age, const char* _name): age(_age), name(_name) {}
friend ostream& operator <<(ostream& out, const Animal& a) {
return out<<a.name<<endl<<a.age<<endl;
}
};
istream& operator >>(istream& in, Animal& a) {
AnimalName n;
int age;
if( in >> n >> age )
a = Animal(age, n);
return in;
}
int main() {
Animal a { 23, "bobo" };
cout<<a;
cin>>a;
cout<<a;
return 0;
}
This way you get to follow the "rule of zero" (basically, classes that do not have the sole responsibility of managing memory/resources should not manage memory and therefore should not implement copy/move-constructors, assignments, or destructors.)
And that takes us to the real reason why you should use std::string: it not only does the memory management for you, but it also takes good care of your IO needs, eliminating the need for a "maximum animal name" in your example:
#include<iostream>
#include<string>
using namespace std;
class Animal {
protected:
string name; // name first, for exception-safety on auto-gen assignment?
int age;
public:
// initialize an "empty" Animal
Animal() : age(0) {}
// initialize an animal by age and name
Animal(int _age, const string& _name): age(_age), name(_name) {}
friend ostream& operator <<(ostream& out, const Animal& a) {
return out<<a.name<<endl<<a.age<<endl;
}
};
istream& operator >>(istream& in, Animal& a) {
string n;
int age;
if( getline(in, n) >> age )
a = Animal(age, n);
return in;
}
int main() {
Animal a { 23, "bobo" };
cout<<a;
cin>>a;
cout<<a;
}

A simple fix is to use std::string for your strings.
It almost doesn't matter what the specific errors you get are. But just to cover that, already in the constructor of Animal,
Animal()
{
name=new char[1];
age = 0;
strcpy(name," ");
}
you have Undefined Behavior by allocating just a single element array and then using strcpy top copy two char values there. Overwriting some memory after the array.
Then in operator>> the UB trend continues.
And so forth.
Use std::string.

Your memory management is wrong, which is corrupting the memory. You are allocating space for one character for name. But
strcpy(name," ");
will pass beyond the memory you allocated, since cstring is null terminated, it will put actually two character, effectively corrupting your memory ( you are accessing memory that is not allocated by your program). It itself has undefined behavior.
Further you are deleting an apparently unknown amount of memory in the destructor, which has also undefined behavior.

There are several bugs in your code.
The first one is in the constructor
Animal()
{
name=new char[1];
age = 0;
strcpy(name," ");
}
String literal " " consists from two characters: the space character and the terminating zero '\0;. So you need to allocate dynamically 2 bytes
name=new char[2];
that to use after that function strcpy.
Or instead of string literal " " you should use "an empty" string literal "" that contains only the terminating zero '\0'.
The other bug in function
istream& operator >>(istream& in, Animal& a)
{
in>>a.name>>a.age;
return in;
}
As you initially allocated only 1 byte pointed to by name then you may not use operator
in>>a.name;
because you will overwrite memory that does not belong to the allocated extent.
For example you could define the operator the following way
std::istream& operator >>( std::istream& in, Animal &a )
{
char itsName[25];
in >> itsName >> a.age;
char *tmp = new char[std::strlen( itsName ) + 1];
std::strcpy( tmp, itsName );
delete [] name;
name = tmp;
return in;
}
In this case you could enter a name that does not exceed 24 characters.
Take into account that you need also to define a copy constructor and the copy assignment operator if you are going to assign one object to another.

Related

Operator overloading using deep copy in c++

#include<iostream>
#include<cstring>
#include<conio.h>
using namespace std;
class String
{
char *value;
int len;
public:
String()
{
len=0;
value=0;
}
~String() {}
String(char *s)
{
len=strlen(s);
value=new char[len+1];
strcpy(value,s);
}
String(String & s)
{
len=s.len;
value=new char[len+1];
strcpy(value,s.value);
}
friend String operator+(String obj1, String obj2)
{
String obj3;
obj3.len=obj1.len+obj2.len;
obj3.value=new char [obj3.len+1];
strcpy(obj3.value,obj1.value);
strcat(obj3.value,obj2.value);
return obj3;
}
friend String operator=(String obj1, String obj2)
{
String obj3;
strcpy(obj3.value,obj1.value);
strcat(obj3.value,obj2.value);
return obj3;
}
void display()
{ cout<<value<<endl; }
};
int main()
{
String s1("Bodacious ");
String s2("AllienBrain");
String s3;
s3=s1+s2;
s3.display();
getch();
}
As I am already operated the operator + in my code but i also want to overload the operator= to conactenate both the strings but this code shows no error when i overload the + operator but it shows the correct output i.e. Bodacious AllienBrain.
But when i overload the operator= it throws error so anyone tell me whats wrong with me?
More appropriate version of overloaded = operator would be as below:
class String
{
///...
String& operator=(const String& obj2)
{
if(this->value ){
delete this->value; // Free if existing
this->value = NULL;
}
len = obj2.len;
this->value = new char[len + 1];
strcpy(this->value, obj2.value);
return *this;
}
///
};

c++ destructor called before cout

#include <iostream>
#include <string.h>
using namespace std;
class Location
{
double lat, lon;
char *emi;
public:
Location(int =0, int=0, const char* =NULL);
~Location();
Location (const Location&);
void print () const;
friend ostream& operator<< (ostream&, const Location &);
void operator! ();
protected: ;
private:
};
Location::Location(int lat, int lon, const char *emi)
{
this->lat=lat;
this->lon=lon;
if (emi!=NULL)
{
this->emi=new char [strlen (emi)+1];
strcpy (this->emi, emi);
}
}
Location::~Location()
{
if (emi!=NULL)
delete []emi;
}
Location::Location(const Location &l)
{
lat=l.lat;
lon=l.lon;
if (l.emi!=NULL)
{
emi=new char [strlen (l.emi)+1];
strcpy (emi, l.emi);
}
}
void Location::operator! ()
{
if (!(strcmp(this->emi, "north")))
strcpy (this->emi, "south");
else strcpy (this->emi, "north");
}
void Location::print() const
{
cout<<this->emi<<endl;
cout<<this->lon<<endl;
cout<<this->lat<<endl;
cout<<endl;
}
class Adress
{
char *des;
Location l1;
char *country;
public:
Adress(char *,const Location &, char *);
virtual ~Adress();
friend ostream& operator<< (ostream&, const Adress &);
protected:
private:
};
Adress::Adress(char *des,const Location &l1, char *country)
{
if (des!=NULL)
{
this->des=new char [strlen (des)+1];
strcpy (this->des, des);
}
if (country!=NULL)
{
this->country=new char [strlen (country)+1];
strcpy (this->country, country);
}
this->l1=l1;
}
Adress::~Adress()
{
if (country!=NULL)
delete []country;
if (des!=NULL)
delete []des;
}
ostream& operator<< (ostream &os, const Adress& a){
os <<"Descrition: " << a.des<<endl;
os<<"Country: "<<a.country<<endl;
a.l1.print();
return os;
}
int main ()
{
Adress a1 ("dsad", Location (323, 34, "fdsf"), "fsdf");
cout<<a1;
}
The problem is that when I create an Adress object and display it, all the fields are correct , but the "emi" which is messed up, showing a random character. I think the destructor is called before I display it. If I remove the Location destructor it works. How should I resolve it? I'm sorry for my mistakes but I am a newbie.
First of all, it would be better to use std::string rather than char* but I will explain your problem for the education goal.
You must ensure after constructing an object, all of its member variables are initialized. In the case of Location class for example; you did not initialize the emi member variable if the third argument of the constructor is nullptr. so I changed it a little:
Location::Location(int _lat, int _lon, const char* _emi)
: lat(_lat)
, lon(_lon)
, emi(nullptr)
{
if (_emi != nullptr)
{
emi = new char[strlen(_emi) + 1];
strcpy(emi, _emi);
}
}
Next, you have a raw pointer in your class and you can not simply copy or assign it. You have to implement the assignment operator as well as the copy constructor.
Location& Location::operator=(const Location& other)
{
if (this != &other)
{
lat = other.lat;
lon = other.lon;
if (emi) delete[] emi;
emi = new char[strlen(other.emi) + 1];
strcpy(emi, other.emi);
}
return *this;
}

Overloading the << operator and problems with add function

So I recently got back into programming, in prep for my last year of physics and i'm having some trouble with an example.
It says I need to add the specific Exhibit class shown in the code, and then implement the functions. Then it wants me to display this as the output;
Max says Eeeeep
Jack says Roar
Petals says Woof
{ Max Jack Batty Sheepy Hippopotamusesy Turkey }
{ Max Jack Batty Sheepy Hippopotamusesy Turkey }
By overloading the << operator.
I've had a go, but im just stuck I believe i'm also doing the add() function incorrectly, so if someone could provide me with some insight it would be much appreciated.
Code:
#include <iostream>
#include <string>
using namespace std;
//------------------------------------------------------------
class Animal
{
private:
string name;
string sound;
public:
Animal(const string& n = "", const string& s = "");
~ Animal();
string getName() const {return name;}
string makeSound() const{return sound;}
};
Animal::Animal(const string&n, const string &s)
{
name=n;
sound=s;
}
Animal:: ~ Animal() //Destructor
{
}
ostream &operator <<(ostream& out, Animal & obj)
{
cout <<obj.getName()<<" says "<<obj.makeSound() ;
return out;
}
//------------------------------------------------------------
class Exhibit{
public:
Exhibit(int maxEnt = 10);
~Exhibit();
void add(const Animal& e);
Animal& operator [](size_t i){ return entries[i];};
friend ostream& operator <<(ostream& out, const Exhibit& obj);
private:
int MaxNumberOfAnimals;
int CurrentNumberOfAnimals;
Animal* entries;
};
Exhibit::Exhibit(int maxEnt)
{
MaxNumberOfAnimals=maxEnt;
}
Exhibit:: ~ Exhibit()
{
}
void add(const Animal& e)
{
int j=0;
entries[j]=e;
j++;
return;
}
ostream& operator <<(ostream& out, const Exhibit& obj)
{
cout <<"{ ";
for(int i=0;i++;i!=obj.MaxNumberOfAnimals)
{
cout << obj.entries[i] << ", ";
}
cout << "}"<<endl;
return out;
}
//------------------------------------------------------------
int main()
{
Animal* monkey = new Animal("Max", "Eeeeep");
Animal* tiger = new Animal("Jack", "Roar");
Animal* Dog = new Animal("Petals", "Woof");
cout << *monkey <<endl;
cout << *tiger <<endl;
cout << *Dog << endl;
Exhibit cage(10);
cage.add(*monkey);
cage.add(*tiger);
/*cage.add(Animal("Batty", "Screech"));
cage.add(Animal("Sheepy", "Bleat Bleat"));
cage.add(Animal("Hippopotamusesy", "growl"));
cage.add(Animal("Turkey","Gobble"));*/
cout << cage << endl;
cout << cage << endl;
delete monkey;
delete tiger;
delete Dog;
system("pause");
return 0;
}
//------------------------------------------------------------
One things that could be causing the problem with your << overloading is that you are sending everything to cout, rather than the out stream you pass in. A potential fix there could be changing "cout" to "out", as below:
ostream &operator <<(ostream& out, Animal & obj)
{
out <<obj.getName()<<" says "<<obj.makeSound() ;
return out;
}
As for your add function in your Exhibit class, it looks like you are initializing your counter 'j' each time you call the function, which means that it will always be 0. You can fix this by making your counter a private data member of the Exhibit class, or perhaps just by using your 'int CurrentNumberOfAnimals' to keep track, like below:
void add(const Animal& e)
{
entries[CurrentNumberOfAnimals]=e;
CurrentNumberOfAnimals++;
return;
}
There may be other errors in there that I didn't notice, but there are some.
Edit:
As posted in an above answer, entries is not initialized correctly. If you can, use a vector of Animals, like below:
#include <vector> //This should be in your Exhibit header file
class Exhibit{
public:
Exhibit(int maxEnt = 10);
~Exhibit();
void add(const Animal& e);
Animal& operator [](size_t i){ return entries[i];};
friend ostream& operator <<(ostream& out, const Exhibit& obj);
private:
int MaxNumberOfAnimals;
int CurrentNumberOfAnimals;
vector<Animal> entries;
};
This also simplifies your add function, though, see below:
void add(const Animal& e)
{
entries.push_back(e);
return;
}
2nd Edit:
So far as I can tell, this is how to do it using arrays. This is assuming that you need at most 10 animals in your "entries" array, since you have "int maxent = 10" in your original Exhibit class.
Your Exhibit class declaration could look like this (we shouldn't need the "int maxent" in your constructor anymore unless you need it for other reasons):
class Exhibit{
public:
Exhibit();
~Exhibit();
void add(const Animal& e);
Animal& operator [](size_t i){ return entries[i];};
friend ostream& operator <<(ostream& out, const Exhibit& obj);
private:
int MaxNumberOfAnimals;
int CurrentNumberOfAnimals;
Animal entries[10];
};
Your add function could then look like the first add function above, repasted here for convenience:
void add(const Animal& e)
{
entries[CurrentNumberOfAnimals]=e;
CurrentNumberOfAnimals++;
return;
}
entries[j] = e; - that's undefined behavior right there, because entries is uninitialized pointer (which results from junk data) and you're trying to access memory location which is pointed to by that invalid pointer.
If you can use std::vector, use it. Otherwise, your Exhibit class would need to partially implement one - allocation, deallocation, reallocation, deep copy if you want copyable Exhibit...

Cout not returning any output

I'm learning c++ and found a problem that I dont understand. I have this simple code:
#include <iostream>
#include <vector>
class Person
{
string * name;
public:
Person(const string & n) : name {new string {n}} {}
~Person() {delete name;}
string getName() const {return *name;}
};
int main()
{
vector<Person> people;
people.push_back(Person("Tom"));
cout << "Name is: " << people.back().getName() << endl;
return 0;
}
When I run it, I have no output. Dont know why? However, when I do simillar, but without vector everything is ok:
int main()
{
Person tom {"Tom"};
cout << "Name is: " << tom.getName() << endl;
return 0;
}
As other said, better to do without pointers. However, if you wondering what is happening, the reason for what you get is that in this line people.push_back(Person("Tom")); Person object is created, and its copy passed to vector. However, once the object is copied, destructor is executed which deletes the string.
With your usage of pointers, both the original Person object and its copy point to the same string in memory. String gets deleted by destructor, and name pointer in the copy does not point to anything. Thus you get undefined behaviour.
To rectify this issue, either dont use pointers, or you need to define your own copy constructor. For example:
class Person
{
string * name;
public:
Person(const string & n) : name {new string {n}} {}
// copy constructor which makes new string in memory
//based on the original string.
Person(const Person & other) {
name = new string(other.getName());
}
~Person() { delete name; }
string getName() const {return *name;}
};
You are using wrong type. Your string is already a string type. Write the code like this.
class Person
{
public:
Person (const string& n) : name(n) { }
~Person() {}
string getName() const { return name; }
private:
string name;
};
If you insist to use pointer in your member variable, you shall overwrite the copy constructor and overload assign operator.
Give you an example:
class Person
{
public:
Person (const char* n) : name(new char[strlen(n)+1]) { strcpy(name, n); }
Person (const Person& p) : name(new char[strlen(p.name)+1]) { strcpy(name, p.name); }
~Person() { delete [] name; }
Person& operator=(const Person& p)
{
if ( &p == this ) return *this;
delete [] name;
name = new char[strlen(p.name)+1];
strcpy(name, p.name);
return *this;
}
string getName() const { return name; }
private:
char* name;
};
The reason why your code is buggy has already been explained, but if you have C++11, you can use emplace_back:
people.emplace_back("Tom");
Still, using pointers instead of a plain member variable is just unnecessarily complicating your program. The less memory management you have to do, the better. Read about the Rule of Zero. Better yet, if your getName() function doesn't do anything special, remove it and just make name public.

Overloading istream operator with dynamic memory allocation

Hello so I am confused with my istream& operator>>. I have to overload this operator to take input for a class that is using dynamic memory allocation for a C string.
My Employee.h file is
#include <iostream>
using namespace std;
const double MIN_WAGE = 10.25;
class Employee {
int num;
char * name;
double rate;
public:
Employee();
Employee(const Employee&);
Employee operator=(const Employee&);
friend istream& operator>>(istream& is, Employee& employee);
friend ostream& operator<<(ostream& is, const Employee& employee);
friend bool operator>(const Employee& a, const Employee& b);
~Employee();
};
I have a copy constructor which called the assignment operator
Employee::Employee(const Employee & e) {
name = NULL;
*this = e;
}
Employee Employee::operator=(const Employee & e) {
if (this != e) {
num = e.num;
rate = e.rate;
if (name != NULL) delete [] name;
if (e.name != NULL) {
name = new char[strlen(e.name) + 1];
strcpy(name, e.name);
}
else name = NULL;
}
return *this;
}
And in the assignment operator I have dynamically assigned memory for the length of the C string I am using. My istream function so far:
istream& operator>>(istream& is, Employee & e) {
int n;
double r;
}
My question is: how do I use the new dynamic memory allocation in my assignment operator in my istream function?
Just change the name data member of class Employee from const char* to std::string and you will not need to override operator= anymore :)
Note that it's a good practice to avoid dynamic allocation as much as possible. Try to take advantage of using objects with automatic storage duration and learn more about RAII idiom. Your code will become easier to read and less vulnerable for memory leaks :)
Disclaimer: both solution are for educational purpose and I would not recommend to use it in any real program. If you need to solve homework with strict requirements, then that maybe ok:
First:
istream& operator>>(istream& is, Employee & e) {
Employee tmp;
tmp.name = new char[1024];
is >> tmp.num >> tmp.rate >> tmp.name;
e = tmp;
return is;
}
Second - more ugly and more "effective" solution:
istream& operator>>(istream& is, Employee & e) {
char buffer[1024];
Employee tmp;
tmp.name = buffer;
is >> tmp.num >> tmp.rate >> tmp.name;
e = tmp;
tmp.name = 0;
return is;
}
Again both solution created under condition "to use existing assignment operator", real code should be different.
Note:
if (name != NULL) delete [] name;
is redundant, write
delete [] name;
instead