C++ (Logical Copy Constructor) How do I copy an object? - c++

I have a class:
class Person{
public:
Person();
~Person();
string name;
int* age;
};
int main()
{
Person* personOne = new Person;
personOne->name = "Foo";
personOne->age = new int(10);
return 0;
}
How do I create another object of Person that copies all of personOne data? The age pointer needs to be pointing to a new int so whenever the age changes in personOne or personTwo, it doesn't affect each other.

There are two posibilites:
copy constructor + assignment operator
clone method
Code:
class Person{
public:
Person();
~Person();
Person (const Person& other) : name(other.name), age(new int(*(other.age)))
{
}
Person& operator = (const Person& other)
{
name = other.name;
delete age; //in case it was already allocated
age = new int(*(other.age))
return *this;
}
//alternatively
Person clone()
{
Person p;
p.name = name;
p.age = new int(age);
return p;
}
string name;
int* age;
};
Answer these before going forward:
do you really need an int pointer?
are you aware of smart pointers?
do you free all memory you allocate?
do you initialize all members in the constructor?

Related

In Interview, I asked to write constructor, Copy constructor and assignment operator

In Interview, I asked to write constructor, Copy constructor and assignment operator. I wrote the following code.
He then asked me what is wrong in following code which I unable to answer, could you help me to know what is wrong?
Also, From question what interviewer was trying to find?
//constructor, copy constructor, assignment operator and destructor
class Employee
{
int id;
char *name;
public:
//constructor
Employee()
{
id=0;
*name = new char[];
}
//Copy constructor
Employee (const Employee& oldObj)
{
id = oldObj.id;
*name = *(oldObj.name);
}
//destructor
~Employee()
{
delete[] name;
}
//Assignment operator overloading
void operator = (const Employee& obj)
{
id = obj.id;
delete[] name;
*name = *(obj.name);
}
};
int main()
{
Employee a1;
Employee a2 = a1; //copy constructor
Employee a3;
a3 = a1;//assignment operator
}
what is wrong in following code
Using bare owning pointers instead of smart pointers or containers.
*name = pointer is ill-formed.
new char[] is ill-formed.

Overloading operator+ C++

I see multiple examples on how to add two variables of the same class, but I can't find examples on how to add variables of different classes with overloading.
We haven't worked with templates yet if it is relevant to my problem, but I am assuming we are not allowed to use it.
I want to add class book to class person with the + operator.
ex. Person p; Book b; p+b;//adds a book like it would if it were using a function like p.addBook(b);
class Person
{
private:
B * b;
string sName;
int numOfBook;
int maxNumOfBooks;
maxNumOfBooks is the max that person is allowed to carry
public:
Person();
Person(const Person&);
~Person();
const Person operator = (const Person &)
void addBook(const Book &); //adds a book to a Person
Person operator+(Person );
Book getBook(){return b[numOfBook];}//returns index of book
};
Person::Person()
{
numOfBook = 0;
maxNumOfBooks = 10;
b = new B[maxNumOfBooks];
for(int x=0;x<maxNumOfBooks;x++)
{
sets name to "". I did overload this.
b[x] = "";
}
}
Person::Person(const Person& p)
{
numOfBook = p.numOfBook;
maxNumOfBooks = p.maxNumOfBooks;
b = new B[maxNumOfBooks];
for(int x=0;x<maxNumOfBooks;x++)
{
b[x] = p.b[x];
}
}
Person::~Person()
{
delete [] b;
}
void Person::addBook()
{
replaces "" if numOfBooks < maxNumOfBooks;
Did code this.
}
Person Person::operator+(Person obj)
{
not entirely sure what to do here. but I am assuming it must look something like this.
Spell a;
++numOfBook;
a=this->getBook(numOfBook);
obj.addBook(a);
return *this;
}
const Person Person::operator = (const Person & obj)
{
delete [] b;
numOfBook = p.numOfBook;
maxNumOfBooks = p.maxNumOfBooks;
b = new B[maxNumOfBooks];
for(int x=0;x<maxNumOfBooks;x++)
{
b[x] = p.b[x];
}
return *this;
}
class Book
{
private:
string name;
int price;
public:
void setP();
int getP();
};
int main()
{
Person s;
Book bk("Story", 18);
s+bk;
I want to add a book at Person, but it should also make use of the addBook() function.
}
When you overload the + operator, you return a new instance of the return type (often this is overloaded for arithmetic type where the two inputs and output are the same type, but that's not at all necessary).
You just need to figure out what the operator is supposed to do.
In this case, the + operator should add a book to a copy of the person object called on (i.e. return a new instance and not modify the origional).
Person Person::operator + (const Book& b){
Person tmp(*this); //create a copy of this Person to modify
tmp.addBook(b); //modify this new copy and not the original
return tmp; //return the modified copy
}
whereas the += operator will modify the current object and return a reference
Person& Person::operator += (const Book& b){
addBook(b);
return *this;
}
If you already have += defined you can change your + operator to:
Person Person::operator + (const Book& b){
return Person(*this) += b;
}
Note:
Person P;
Book B;
P + B; // P is not modified;
Is not equivalant to:
Person P;
Book B;
P.addBook(B); // P is modified
However the following is.
Person P;
Book B;
P += B; // P is modified
As well as
Person P;
Book B;
P = P + B; // P is modified because it is assigned (P + B)

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.

C++ class destructor not working properly

#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.

Can this Assignment and Copy cnstr be organized better

I am trying to improve my understanding of the copy constructor and copy assign. operator
Here is a simple class that I came up with
class Bar
{
char* name;
int zip;
std::string address;
public:
Bar(const Bar& that)
{
//Copy constructor
size_t len = strlen(that.name + 1);
name = new char[len];
strcpy(name, that.name);
//Copy the zip
this->zip = that.zip;
//Copy the address
this->address = that.address;
}
Bar& operator=(const Bar& that)
{
//Assignment operator
if(this != &that)
{
//Copy the name
size_t len = strlen(that.name + 1);
name = new char[len];
strcpy(name, that.name);
//Copy the zip
this->zip = that.zip;
//Copy the address
this->address = that.address;
}
return *this;
}
};
My question is since the code in the copy constructor and copy assignment operator are the same does it make more sense to unify that into a deep copy method so that incase I add another member variable I dont have to add another line to the copy cnstr and copy assign. section ? Any suggestions ?
The "normal" way of doing things where you manage your own resources is a little different:
char* cppstrdup(const char*s, int len=0);
class Bar
{
char* name;
int zip;
std::string address;
public:
Bar(const Bar& that)
:name(nullptr),
zip(that->zip),
address(that->address)
{
name = cppstrdup(that.name); //done here for exception safety reasons
}
Bar(Bar&& that) //if you have C++11 then you'll want this too
:name(nullptr)
{
swap(*this,that);
}
~Bar() //you forgot the destructor
{
delete [] name;
}
Bar& operator=(Bar that) //this is called copy and swap.
{ //"that" is a copy (notice, no & above), and we simply swap
swap(*this,that);
return *this;
}
friend void swap(Bar& left, Bar& right)
{
using std::swap;
swap(left.name, right.name);
swap(left.zip, right.zip);
swap(left.address, right.address);
}
};
//uses new instead of malloc
inline char* cppstrdup(const char* s, int len)
{
if (s==0) return nullptr;
if (len==0) len = strlen(s);
char* r = new char[len+1];
strncpy(r, len+1, s);
r[len] = 0;
return r;
}
The benefits of this pattern is that it is much easier to get exception safety, often with the strong exception guarantee.
Of course, even more normal is to not use char* name, and obey the "Rule of Zero" isntead. In which case, it becomes VERY different:
class Bar
{
std::string name;
int zip;
std::string address;
public:
Bar() = default; //well, that's easy
};
Check COPY & SWAP idiom. In short - your logic goes into copy constructor and swap method and your assignment operator looks like:
Bar& operator=(const Bar& that)
{
Bar temp(that);
swap(*this, temp);
return *this;
}