This question already has answers here:
What is The Rule of Three?
(8 answers)
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 7 months ago.
Please explain to me why the destructor gets called on the "strand" object after its assigned with "tari" members. this question has been puzzling me and I haven't been able to move on from it as it gives me the wrong answer of 5 at the end instead of 10 when i try to add the contents of the "jesu" objects because the destructor gets called and any operation done on strand object becomes obsolete
#include <iostream>
#include <iomanip>
using namespace std;
class Bheki
{
private:
string name;
int sam;
double *jesu = nullptr;
public:
Bheki(){}
void Hvo()
{
jesu = new double(5);
}
double* getJesu() const
{
return jesu;
}
void setName(string Gama){name = Gama;}
void setSam(int ram){sam=ram;}
string getName()const{
return name;
}
int getSam()const{
return sam;
}
~Bheki(){
delete jesu;
jesu = nullptr;
}
const Bheki operator=(const Bheki &);
const Bheki operator+(const Bheki &);
};
const Bheki Bheki::operator+(const Bheki &ned)
{
Bheki temp;
temp.sam = sam + ned.sam;
temp.jesu = new double();
*temp.jesu = (*jesu) + (*ned.jesu);
cout<<*temp.jesu<<endl;
return temp;
}
const Bheki Bheki::operator =(const Bheki &pop)
{
if(this != &pop)
{
this->name = pop.name;
this->sam =pop.sam;
this->jesu = new double{};
*this->jesu = *pop.jesu;
}
return *this;
}
int main()
{
Bheki *tari = new Bheki();
tari->Hvo();
tari->setName("jece");
tari->setSam(6969);
cout<<tari->getName()<<endl;
Bheki *strand = new Bheki;
*strand = *tari;
cout<<strand->getName()<<endl;
strand->setName("Kumkani");
cout<<tari->getName()<<endl;
*strand->getJesu()=20.325;
cout<<strand->getJesu()<<endl;
cout<<tari->getJesu()<<endl;
*strand->getJesu() = 32;
cout<<*tari->getJesu()<<endl;
strand->setSam(884);
tari-> setSam(7);
cout<<*tari->getJesu()<<endl;
*strand->getJesu() = 5;
*tari->getJesu() = 5;
Bheki *balo = new Bheki;
*balo = *strand + *tari;
cout<<*balo->getJesu()<<endl;
cout<<balo->getSam()<<endl;
// delete tari;
// delete balo;
// delete strand;
return 0;
}
I compare my code with another post I found posted by #Serge Rogatch who said that it does not get called at assignment but mine does get called which completely contradicts each other
#include <iostream>
#include <string>
class Test
{
std::string _name;
public:
Test(std::string name ="") : _name(name) { }
~Test()
{
std::cout << "Destructor " << _name << std::endl;
}
Test& operator=(const Test& fellow)
{
// avoid changing the name of the object
std::cout << "Assignment operator "
<< _name << "=" << fellow._name << std::endl;
return *this;
}
};
int main()
{
Test t1, t2("t2");
t1 = t2;
return 0;
}
Related
How do I create a copy constructor that copies an object to the newly instantiated object?
Also, how do I use the == operator to compare the speed of two Tricycle
objects?
Here is the sample code:
#include <iostream>
class Tricycle
{
private:
int* speed;
//static int speedlim;
public:
Tricycle();
// copy constructor and destructor use default
int getSpeed() const { return *speed; }
//static int getspeedlim() {return speedlim;}
void setSpeed(int newSpeed) { *speed = newSpeed; }
Tricycle operator=(const Tricycle&);
void operator++();
void operator++(int);
};
//Tricycle operator++();
//Tricycle operator++(Tricycle,int);
Tricycle::Tricycle()
{
speed = new int;
*speed = 5;
//speedlim=40;
}
Tricycle Tricycle::operator=(const Tricycle& rhs){
if (this == &rhs)
return *this;
delete speed;
//speed = new int;
//*speed = rhs.getSpeed();
return *this;
}
void Tricycle::operator++(){
(*speed)++;}
void Tricycle::operator++(int){
(*speed)++;}
int main()
{
Tricycle wichita;
//std::cout << "the speed limit is : " << Tricycle::getspeedlim();;
std::cout << "Wichita's speed : " << wichita.getSpeed() << "\n";
std::cout << "Setting Wichita's speed to 6 ...\n";
wichita.setSpeed(6);
Tricycle dallas;
std::cout << "Dallas' speed : " << dallas.getSpeed() << "\n";
std::cout << "Copying Wichita to Dallas ...\n";
wichita = dallas;
std::cout << "Dallas' speed : " << dallas.getSpeed() << "\n";
wichita.setSpeed(5);
wichita++;
std::cout << "Wichita's speed : " << wichita.getSpeed() << "\n";
wichita.setSpeed(5);
++wichita;
std::cout << "Wichita's speed : " << wichita.getSpeed() << "\n";
return 0;
}
Here is a test I tried using pre-increment and post-increment to increment the speed. This is the answer:
Wichita's speed : 5
Setting Wichita's speed to 6 ...
Dallas' speed : 5
Copying Wichita to Dallas ...
Dallas' speed : 5
Wichita's speed : 6
Wichita's speed : 6
How do I create a copy constructor that copies an object to the newly instantiated object?
Like this:
class Tricycle
{
...
public:
...
Tricycle(const Tricycle&);
...
};
Tricycle::Tricycle(const Tricycle &src)
{
speed = new int;
*speed = src.getSpeed(); // or: *(src.speed)
// or:
// speed = new int(src.getSpeed()); // or: *(src.speed)
}
Also, how do I use the == operator to compare the speed of two Tricycle objects?
Like this:
class Tricycle
{
...
public:
...
bool operator==(const Tricycle&) const;
...
};
bool Tricycle::operator==(const Tricycle &rhs) const
{
return getSpeed() == rhs.getSpeed();
// or:
// return *speed == *(rhs.speed);
}
That said, there are some other problems with your code:
Tricycle is missing a destructor to free the allocated speed, thus leaking it. The default compiler-generated destructor is not sufficient, just like the copy constructor and assignment operator.
the assignment operator= and pre-increment operator++ need to return a Tricycle& reference to *this, and the post-increment operator++ needs to return a Tricycle copy of *this before speed was updated. See What are the basic rules and idioms for operator overloading?
the assignment operator= is leaving speed in an invalid state. It is freeing speed but not reallocating it or setting it to NULL. There is no reason for it to free speed at all, just set its value.
Try this:
class Tricycle
{
private:
int* speed;
//static int speedlim;
public:
Tricycle();
Tricycle(const Tricycle&);
~Tricycle();
int getSpeed() const { return *speed; }
//static int getspeedlim() { return speedlim; }
void setSpeed(int newSpeed) { *speed = newSpeed; }
Tricycle& operator=(const Tricycle&);
Tricycle& operator++();
Tricycle operator++(int);
};
//int Tricycle::speedlim = 40;
Tricycle::Tricycle()
{
speed = new int(5);
}
Tricycle::Tricycle(const Tricycle &src)
{
speed = new int(*(src.speed));
}
Tricycle::~Tricycle()
{
delete speed;
}
Tricycle& Tricycle::operator=(const Tricycle& rhs)
{
if (this != &rhs)
*speed = rhs.getSpeed();
return *this;
}
Tricycle& Tricycle::operator++()
{
++(*speed);
return *this;
}
Tricycle Tricycle::operator++(int)
{
Tricycle temp(*this);
++(*speed);
return temp;
}
That said, there is no good reason for speed to be dynamically allocated at all. If you get rid of that allocation, then the default compiler-generated destructor, copy constructor, and copy assignment operator will be sufficient, so you don't need to define them at all:
class Tricycle
{
private:
int speed;
//static int speedlim;
public:
Tricycle();
int getSpeed() const { return speed; }
//static int getspeedlim() { return speedlim; }
void setSpeed(int newSpeed) { speed = newSpeed; }
Tricycle& operator++();
Tricycle operator++(int);
};
//int Tricycle::speedlim = 40;
Tricycle::Tricycle()
{
speed = 5;
}
Tricycle& Tricycle::operator++()
{
++speed;
return *this;
}
Tricycle Tricycle::operator++(int)
{
Tricycle temp(*this);
++speed;
return temp;
}
I get a blank output. I'm a newbie and have been struggling on this for some time.
I have gotten 0 errors by the compiler.
Also what can be improved on this?
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
#include <iostream>
#include <cassert>
class String
{
private:
char* Str_Buffer{};
int Str_Size{};
public:
String(const char* string = " ")
: Str_Size{ static_cast<int>(strlen(string)) }
{
Str_Buffer = new char[Str_Size];
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (int index{ 0 }; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}
I get a blank output.
You don't fill your String::Str_Buffer with meaningful data in the constructor. You could use std::strcpy() from <cstring> to do that. std::strlen() is also declared in that header file. To use std::strcpy() the memory pointed to by String::Str_Buffer needs to be one char bigger than the string you want to copy there because strings in C and C++ are zero-terminated ('\0').
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
Why would you want an int? Sizes of objects in C++ are measured with values of type std::size_t (defined in several headers but when in doubt include <cstddef>). std::size_t is guaranteed to be big enough to handle all object sizes. It is for example the return type of std::strlen() and the sizeof-operator.
Your assignment operator is not exception-safe:
String& operator=(const String& string)
{
// ...
delete[] Str_Buffer; // the old state is now gone
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size]; // when new[] throws, the object
// will be in an undefined state
// ...
Possible but not elegant solution:
String& operator=(const String& string)
{
char *temp = new[string.Str_Size];
// copy string.Str_Buffer to temp
delete[] Str_Buffer;
Str_Buffer = temp;
Str_Size string.Str_Size
return *this;
}
See Copy-and-Swap for an better solution.
Resource Management
Please familiarize yourself with The Rule of Five and the Copy-and-Swap Idiom.
A starting point for a class that manages a string could look like that:
#include <cassert> // assert()
#include <cstddef> // std::size_t
#include <cstring> // std::strlen(), std::strcpy()
#include <utility> // std::swap(), std::exchange()
#include <iostream>
class string_t
{
size_t length = 0;
char *data = nullptr;
public:
string_t() = default;
string_t(char const *str)
: length { str ? std::strlen(str) : 0 },
data { new char[length + 1]{} }
{
str && std::strcpy(data, str);
}
string_t(string_t const &other) // copy constructor
: length { other.length },
data { new char[length + 1]{} }
{
other.data && std::strcpy(data, other.data);
}
string_t(string_t &&other) // move constructor
: length { std::exchange(other.length, 0) }, // steal others resources and
data { std::exchange(other.data, nullptr) } // give other a state it's
{} // destructor can work with
string_t& operator=(string_t other) // assignment operator
{ // mind: other gets copied
std::swap(length, other.length); // steal other's resources
std::swap(data, other.data); // other's destructor will
} // take care of ours.
~string_t() { delete[] data; }
std::size_t get_length() const { return length; }
char& operator[](std::size_t index)
{
assert(index < length);
return data[index];
}
// stream-insertion operator:
friend std::ostream& operator<<(std::ostream &os, string_t const &str)
{
return os << (str.data ? str.data : "");
}
};
int main()
{
string_t foo{ "Hello!" }; // char const* constructor
std::cout << foo << '\n';
string_t bar{ foo }; // copy constructor
std::cout << bar << '\n';
string_t qux{ string_t{ "World!" } }; // move constructor (from a temporary)
std::cout << qux << '\n';
bar = qux; // assignment operator
std::cout << bar << '\n';
}
First of all, you need to include for strlen. You get a blank output because the constructor does not write the input string to Str_Buffer. You may use std::copy to copy the memory to the allocated buffer.
You have to use static cast, because strlen returns std::size_t. Just change the type of Str_Size to std::size_t to get rid of the static cast.
Also take a look at the rule of five. Defining a move and copy constuctor will improve performace of your code.
See a working version of your code below:
#include <iostream>
#include <cassert>
#include <cstring>
#include <algorithm>
class String
{
private:
char* Str_Buffer;
std::size_t Str_Size;
public:
String(const char* string = " ")
: Str_Size{ strlen(string) }
{
Str_Buffer = new char[Str_Size];
std::copy(string, string + Str_Size, Str_Buffer);
}
String(const String& other)
: Str_Size(other.Str_Size)
{
Str_Buffer = new char[Str_Size];
std::copy(other.Str_Buffer, other.Str_Buffer + Str_Size, Str_Buffer);
}
String(String && other)
{
*this = std::move(other);
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (std::size_t index = 0; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}
I want Swap the name (i.e. string in cName[]) of the two cats using the pointer approach.
However I want to Only swap the name, NOT the object.
Am I correct?
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;
class CAT
{
public:
CAT(char * firstname) { strncpy(cName, firstname, 79); }
~CAT() { ; }
char * getName() { return cName; }
void setName(char *nameinput) { strncpy(cName, nameinput, 79); }
private:
char cName[80];
};
void nameSwap(CAT *CatA, CAT *CatB)
{
char testing[] = "testing";
CAT temp =CAT(testing);
temp = *CatA;
*CatA = *CatB;
*CatB = temp;
}
int main()
{
char Taby[] = "Taby";
char Felix[] = "Felix";
CAT pA = CAT(Taby);
CAT pB = CAT(Felix);
cout << "The inital name pA is " << pA.getName() << " and pA is" << pB.getName() << endl;
nameSwap(&pA, &pB);
cout << "After approach" << endl;
cout << "The name pA is " << pA.getName() << " and " << pB.getName() << endl;
system("PAUSE");
return 0;
}
You are actually swapping the whole objects, not only the name of the CAT.
If you only want to swap the name, you need to access the cName member in a similar way as you are doing for the objects. You'd also need permission to access to the cName member in such a swap function, which a function outside won't have since cName is private. Make the swap function a member of your class:
class CAT
{
public:
CAT(const char* firstname) { strncpy(cName, firstname, 80); }
~CAT() {}
const char* getName() const { return cName; }
void setName(const char *nameinput) { strncpy(cName, nameinput, 80); }
void swapName(CAT& CatB)
{
char tmp[80];
strncpy(tmp, CatB.cName, 80);
strncpy(CatB.cName, cName, 80);
strncpy(cName, tmp, 80);
}
private:
char cName[80];
// other CAT attributes won't be affected by the name swap
};
And call it like this
pA.swapName(pB); // or pB.swapName(pA); - same result
But consider using std::string instead of char[]. You'll soon find C++ strings much easier to work with and also, when swapping those, only the pointers to the underlying memory is swapped, so it's more effective.
std::string one;
std::string two;
one.swap(two);
Edit: As per request, I added a version using pointers.
I made it in a haste and haven't debugged it so I've probably made a lot of mistakes. First, I made a new class called wong_string that will hold the name and any other attributes suiteable for strings.
#include <stdexcept>
#include <cstring>
class wong_string {
char* m_data;
static char* duplicate(const char* str) {
size_t len = std::strlen(str)+1;
char* rv = new char[len];
std::memcpy(rv, str, len);
return rv;
}
public:
// default constructor: wong_string howdy1;
wong_string() : m_data(nullptr) {}
// conversion constructor: wong_string howdy2("value2");
wong_string(const char* cstr) :
m_data(duplicate(cstr))
{}
// copy constructor: wong_string howdy3 = howdy2;
wong_string(const wong_string& rhs) : wong_string(rhs.m_data) {}
// move constructor: wong_string howdy4 = wong_string("value4");
wong_string(wong_string&& rhs) : m_data(rhs.m_data) {
rhs.m_data = nullptr;
}
// copy assignment operator: (wong_string howdy5;) howdy5 = howdy4;
wong_string& operator=(const wong_string& rhs) {
if(this!=&rhs) {
char* tmp = duplicate(rhs.m_data);
if(m_data) delete []m_data;
m_data = tmp;
}
return *this;
}
// copy assignment operator from c string
wong_string& operator=(const char* rhs) {
*this = wong_string(rhs);
return *this;
}
// move assignment operator: (wong_string howdy6;) howdy6 = wong_string("value6");
wong_string& operator=(wong_string&& rhs) {
if(this!=&rhs) {
m_data = rhs.m_data;
rhs.m_data = nullptr;
}
return *this;
}
// destructor, free memory allocated by duplicate(), if any
~wong_string() {
if(m_data) delete []m_data;
}
// comparisons
bool operator==(const wong_string& rhs) const {
return strcmp(m_data, rhs.m_data)==0;
}
bool operator!=(const wong_string& rhs) const {
return !(*this==rhs);
}
// conversion to a normal c string
operator char const* () const { return m_data; }
// output stream operator
friend std::ostream& operator<<(std::ostream&, const wong_string&);
// input stream operator - not implemented yet
};
with that in place, your CAT can be made into something like this:
class CAT
{
public:
CAT(const char* firstname, const char* nickname=nullptr) :
cName(firstname),
cNickName(nickname?nickname:firstname)
{}
~CAT() {}
const char* getName() const { return cName; }
void setName(const char *nameinput) { cName=nameinput; }
void swapName(CAT& CatB)
{
std::swap(cName, CatB.cName);
}
private:
wong_string cName; // Madame Florence Jenkins III
// other CAT attributes won't be affected by the name swap
wong_string cNickName; // Ms. Miao
};
So, there you have it. Pointers galore...
I need to create 2 classes, and second class must have pointer to first object as its member. My program works, but there are memory leaks, and I can't fix them. I know, that the problem is in constructor or destructor, but I don't know how to fix it. Here is my code:
main.cpp
#include "classes.h"
int main() {
COne firstObj;
CTwo secondObj;
firstObj.setN(10);
firstObj.setS("candies");
firstObj.print();
secondObj.setS("cookies");
secondObj.setP(&firstObj);
cout<<endl;
secondObj.print();
return 0;
}
classes.h
#ifndef CLASSES_H
#define CLASSES_H
#include <iostream>
#include <string>
using namespace std;
class COne {
private:
int n;
string sOne;
public:
COne(int n2create = 0, const string& sOne2create = "");
COne(const COne& obj);
~COne();
COne& operator=(const COne& obj);
void setN(int n2set);
void setS(const string &sOne2set);
int getN() const;
string getS() const;
void print() const;
};
class CTwo {
private:
string sTwo;
COne* p;
public:
CTwo(string sTwo2create = "", COne& p2create = *(new COne()));
CTwo(const CTwo& obj);
~CTwo();
CTwo& operator=(const CTwo& obj);
void setS(string sTwo2set);
void setP(COne* p2set);
string getS() const;
COne* getP() const;
void print() const;
};
#endif
classes.cpp
#include "classes.h"
COne::COne(int n2create, const string& sOne2create) :n(n2create), sOne(sOne2create) {}
COne::COne(const COne& obj) :n(obj.n), sOne(obj.sOne) {}
COne::~COne() {}
COne& COne::operator=(const COne& obj){
n = obj.n;
sOne = obj.sOne;
return *this;
}
void COne::setN(int n2set) {n = n2set;}
void COne::setS(const string& sOne2set) {sOne = sOne2set;}
int COne::getN() const {return n;}
string COne::getS() const {return sOne;}
void COne::print() const {
cout<<"COne object:"<<endl;
cout<<"n = "<<n<<endl;
cout<<"sOne = \""<<sOne<<"\""<<endl;
}
CTwo::CTwo(string sTwo2create, COne& p2create) :sTwo(sTwo2create), p(new COne(p2create)) {}
CTwo::CTwo(const CTwo& obj) :sTwo(obj.sTwo), p(new COne (*obj.p)){}
CTwo::~CTwo() {delete p;}
CTwo& CTwo::operator=(const CTwo& obj){
sTwo = obj.sTwo;
p = obj.p;
return *this;
}
void CTwo::setS(string sTwo2set) {sTwo = sTwo2set;}
void CTwo::setP(COne* p2set) {p = p2set;}
string CTwo::getS() const {return sTwo;}
COne* CTwo::getP() const {return p;}
void CTwo::print() const {
cout<<"CTwo object:"<<endl;
cout<<"sTwo = \""<<sTwo<<"\""<<endl;
cout<<"p: n = "<<p->getN()<<", sOne = \""<<p->getS()<<"\""<<endl;
}
For one, in:
COne& p2create = *(new COne())
You have a memory leak if the argument is default constructed. This is because p2create is a reference to a dynamically allocated object and you then copy it into another dynamically allocated object only to store the latter and never free the former.
Specifically in:
CTwo::CTwo(string sTwo2create, COne& p2create) :sTwo(sTwo2create), p(new COne(p2create)) {}
Your code contains other errors of course. To solve them all, just rewrite your classes to:
struct COne {
int n;
std::string s;
void print() const {
std::cout << "COne object:\n"
<< "n = " << n << '\n'
<< "s = \"" << s << "\"\n";
}
};
struct CTwo {
std::string sTwo;
COne p;
void print() const {
std::cout << "CTwo object:\n"
<< "sTwo = \"" << sTwo << "\"\n"
<< "p: n = "<< p.n << ", s = \"" << p.s <<"\"\n";
}
};
which are functionally equivalent, and then use them as:
int main() {
COne firstObj {10, "candies"};
firstObj.print();
std::cout << '\n';
CTwo secondObj {"cookies", firstObj};
secondObj.print();
return 0;
}
Live demo
void CTwo::setP(COne* p2set) {p = p2set;}
Saves the new pointer, overwriting the previous value of p without deleting any previously allocated memory assigned to p.
I'm having the above issue when I attempt to set a string (stored in a class) equal to another string. I have combed and combed trying to find if I did not initialize any variables, but I can't find such a situation. In debug mod, I get the above error. In release mode it hangs and Win7 looks for a problem, no major abort or retry window. Here's the relevant code, there is another header file with my main program if you feel it should be included, I'll include the line that causes errors. Language is C++ obviously.
//Error occurs in this area:
Car one;
one = two;
one.addExtra ("Windows");
log << "Car one: " << one << endl;
two = Car(one); // call copy constructor.
//I realize when I call the first one = two, there are no extras
//stored int Car one, which is what differs between the two. Remaining
//code. Extras header:
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
class Extras
{
public:
friend class Car;
friend int main();
friend ostream& operator << (ostream& os, const Extras& in);
friend class CarLot;
Extras(const Extras& other);
Extras& operator=(Extras &rhs);
Extras(string in);
Extras();
~Extras();
void modify_ext(string* in);
//string ex_list;
private:
int place;
string *ex_list;
};
//Extras.cpp:
#include "Extras.h"
Extras::Extras(string in)
{
delete ex_list;
ex_list = new string;
place = 0;
//ex_list = new string[4];
(*ex_list) = in;
place++;
}
Extras::Extras()
{
//ex_list = new string[4];
place = 0;
//for(int i = 0; i < 4; i++)
ex_list = new string;
*ex_list = "0";
}
//Overloaded << operator for Extras class to
//easily output array contents
ostream& operator<< (ostream& os, Extras const &in)
{
os << *(in.ex_list);
return os;
}
Extras& Extras::operator=(Extras &rhs)
{
if(this != &rhs)
{
//string temp;
//temp = rhs.ex_list;
modify_ext(rhs.ex_list);
cout << endl << endl << ex_list << endl << endl;
place = rhs.place;
}
return *this;
}
Extras::Extras(const Extras& other) : place(other.place), ex_list(other.ex_list)
{
//for(int i = 0; i < 4; i++)
//ex_list = other.ex_list;
}
void Extras::modify_ext(string* in)
{
delete ex_list;
ex_list = new string;
(*ex_list).resize((*in).size());
for(unsigned int i = 0; i < (*in).size(); i++)
ex_list[i] = in[i];
}
Extras::~Extras()
{
delete ex_list;
place = 0;
}
//Car Header:
#include "Extras.h"
class Car
{
public:
friend class Extras;
friend Extras& Extras::operator=(Extras &rhs);
friend int main();
friend ostream& operator<< (ostream& os, const Car& in);
friend class CarLot;
friend void add_extra();
~Car();
Car();
Car(Car& other);
Car(string in_name, int in_year, string in_color, float in_cost);
Car& operator=(Car const &rhs);
void edit_extr(int in);
void addExtra(string in);
private:
string name, color;
int year, extr_num;
float cost;
Extras *extr;
};
//Car.cpp:
#include "car.h"
//Constructor
Car::Car(string in_name, int in_year, string in_color, float in_cost)
{
name = in_name;
color = in_color;
year = in_year;
cost = in_cost;
extr = new Extras[3];
extr_num = 0;
}
//Overloaded = operator
Car& Car::operator=(Car const &rhs)
{
if(this != &rhs)
{
name = rhs.name;
color = rhs.color;
year = rhs.year;
cost = rhs.cost;
//delete extr;
extr = rhs.extr;
extr_num = rhs.extr_num;
}
return *this;
}
//Default Constructor
Car::Car()
{
name = "TEMP";
color = "BLUE";
year = 0;
cost = 0;
extr = new Extras[3];
extr_num = 0;
}
//Destructor
Car::~Car()
{
delete extr;
extr = NULL;
}
//Copy constructor
Car::Car(Car& other) : name(other.name), color(other.color), year(other.year),
cost(other.cost), extr_num(other.extr_num)
{
//delete extr;
for(int i = 0; i < extr_num; i++)
{
extr[i].modify_ext(other.extr[i].ex_list);
extr[i].place = other.extr[i].place;
}
}
//Overloaded << operator for Car class
ostream& operator<< (ostream& os, const Car& in)
{
os.precision(2);
os << in.name << ", " << in.year << ", "
<< in.color << ", $"<< in.cost << ", ";
os << "extras include: ";
for(int k = 0; k < in.extr_num; k++)
{
os << in.extr[k] << ", ";
}
os << endl;
return os;
}
void Car::edit_extr(int in)
{
Extras* temp;
temp = new Extras[in];
for(int i = 0; i < in; i++)
temp[i] = extr[i];
extr_num = in;
delete extr;
extr = temp;
}
void Car::addExtra(string in)
{
if(extr_num == 3)
{
//log << "Car has too many extras.";
return;
}
//edit_extr(extr_num + 1);
*(extr[extr_num].ex_list) = in;
extr[extr_num].place++;
extr_num++;
}
As I said, I have one more additional header, another class, and a main program if those need to be included, but I figured this was more than enough code (sorry!) for anyone to look through. Any help would be incredibly appreciated.
What I'm seeing that's broken:
two = Car(one); // call copy constructor.
No, it creates a temporary object with the copy constructor, passes this to operator=() on two, then destroys the temporary.
Extras& operator=(Extras &rhs);
should be:
Extras& operator=(const Extras &rhs);
Extras::Extras(string in)
{
delete ex_list;
ex_list = new string;
place = 0;
//ex_list = new string[4];
(*ex_list) = in;
place++;
}
Better:
Extras::Extras(const string& in): place(1), ex_list(new string(in))
{
}
Extras::Extras(const Extras& other) : place(other.place), ex_list(other.ex_list)
{
//for(int i = 0; i < 4; i++)
//ex_list = other.ex_list;
}
Looking at your default constructor, it's clear that the Extras object owns the string in ex_list. However, this copy constructor claims ownership of the original object's ex_list. It should make its own copy:
Extras::Extras(const Extras& other): place(other.place),
ex_list(new string(other.ex_list))
{
}
void Extras::modify_ext(string* in)
{
delete ex_list;
ex_list = new string;
(*ex_list).resize((*in).size());
for(unsigned int i = 0; i < (*in).size(); i++)
ex_list[i] = in[i];
}
You're copying the string. All you need is:
void Extras::modify_ext(const string* in)
{
*ex_list = *in;
}
Moving on to the Car...
friend class Extras;
friend Extras& Extras::operator=(Extras &rhs);
friend int main();
friend ostream& operator<< (ostream& os, const Car& in);
friend class CarLot;
friend void add_extra();
You should consider refactoring the code to get rid of these.
Car(Car& other);
Car(string in_name, int in_year, string in_color, float in_cost);
Should be:
Car(const Car& other);
Car(const string& in_name, int in_year, const string& in_color, float in_cost);
References are your friends when passing objects to functions.
I'm going to stop here.
In your constructor you are deleting ex_list. It hasn't even been allocated yet, so this is wrong. Remove that and do this instead when allocating your new string:
ex_list = new string(in);
This way you use the string copy constructor. You can get rid of the rest of whatever you were trying to do below in the constructor since this will do it for you.
Edit:
Actually, there are a ton of problems all throughout this code. Is there any reason you want your string to be a pointer internally? You aren't using pointers correctly in a bunch of different places. I only noticed the first one as I scrolled down.
The allocator in debug builds with VC++ uses some magic values to fill allocated areas. In particular, 0xCC is memory that had been allocated but it now freed. So the address 0xCCCCCCD0, looks suspiciously like a small offset (e.g., to a struct or class member) from a pointer in memory that was freed. Given that, Mark Loeser's answer looks promising.