Just as the title states. I don't know where I went wrong. The command prompt window will come up and display my information but then a dialog box comes up saying "Debug Assertion Failed!" How can I go about fixing this? I bet there is a small error somewhere in my code that I can't seem to find....
#include "L10_Circle.h"
int main() {
// output number of circle objects
cout << "Number of circle objects before declarations: " << Circle::getNumObj() << endl;
// create circle objects
Circle c1("c1", 4.0, 1, 1), c2(c1), c3;
// reset c2 properties
c2.setName("c2");
c2.setDiameter(6.0);
// assign c2 properties to c3
c3 = c2;
// reset c3 properties
c3.setName("c3");
c3.setDiameter(8.0);
// display circles
cout << "**Circles***" << endl;
cout << c1 << c2 << c3;
// output number of circle objects
cout << "Number of circle objects after declarations: " << Circle::getNumObj() << endl;
return 0;
}
/*SAMPLE OUTPUT
Number of circle objects before declarations: 0
**Circles***
c3 diameter = 4.00 area = 12.57
c3 diameter = 6.00 area = 28.27
c3 diameter = 8.00 area = 50.27
Number of circle objects after declarations: 3
*/
Your assignment operator is giving the newly created Circle object the exact same pointer value as the one it's copying, so you end up with two Circle objects using the same pointer to a string and thus their destructors call delete twice on the same pointer. This is bad.
As keskiverto suggests, in this case just use a string rather than a pointer to a string.
I totally understand that using a regular string would be easier. My requirement is to use name as a pointer. So again, how can I remedy this error when I run the program?
Does line 85 need to be this?? -> name = new string(*obj.name);
I read that and it didn't help me much.
Can you just show me what I need to do for my particular code? I bet it's just like 2 lines of statements in total or something simple. I just can't seem to get it.
If the requirement is that you simply must use a pointer to access the name...
1 2 3 4 5 6 7 8 9 10
class Circle
{
private:
string* pointerToName;
string actualName;
double diameter;
int centerX;
int centerY;
staticint numObj; // static data shared by all objects
public:
and in each constructor
pointerToName = &actualname;
Using a pointer, no dynamic memory to worry about.
If rhs.name is a pointer to a dynamically-allocated std::string then assigning the pointer will result in the same string object being destroyed by both the lhs and rhs, since after assignment both lhs and rhs's name fields point to the same string. You need to copy the string itself, not merely the pointer to the string.
mbozzi wrote:
Take the second snippet of code in that post and replace SortListClass with Circle.
1 2 3 4 5 6 7
Circle& operator=(Circle const& rhs) {
if (std::addressof(rhs) == this) return *this; // careful for self-assignment
this->~Circle();
new (this) Circle(rhs);
// Edit: need to return something :)
return *this
}
So I made this change to the overloaded operator =
1 2 3 4 5 6 7 8 9 10
// operator overloaded as member function
void Circle::operator = (const Circle &rhs)
{
//copy over values
delete name;
name = new string(*rhs.name);
this->diameter = rhs.diameter;
this->centerX = rhs.centerX;
this->centerY = rhs.centerY;
}
Program seems to run normal now.
Is this a valid way of doing this?
Not quite. If you perform self-assignment, your program exhibits undefined behavior:
1 2
Circle c;
c = c; // deletes *c.name and then tries to copy it into *c.name
It also duplicates code which should be in your destructor (freeing the name) and copy constructor (copying name through the pointer), and is exception-unsafe (along with all the other solutions discussed here).
Also you aren't returning anything from the operator, which means that your class fails to support compound assignment
Ok so I have another question. If this works and I understand now that I have to delete the memory allocated in (name) for this overloaded operator to work.
1 2 3 4 5 6 7 8 9 10
// operator overloaded as member function
void Circle::operator = (const Circle &rhs)
{
//copy over values
delete name;
name = new string(*rhs.name);
this->diameter = rhs.diameter;
this->centerX = rhs.centerX;
this->centerY = rhs.centerY;
}
How come a copy constructor doesn't need to delete the memory before allocating more memory?
Such as this copy constructor..
1 2 3 4 5 6 7 8 9 10
// copy constructor
Circle::Circle(const Circle& obj)
{
name = new string(*obj.name);
diameter = obj.diameter;
centerX = obj.centerX;
centerY = obj.centerY;
numObj++; // increment number of objects created
}
Is it because the object is newly created when the copy constructor is invoked therefore it receives its own memory address for a pointer such as *name?
How come a copy constructor doesn't need to delete the memory before allocating more memory?
The constructor begins the lifetime of a new object, while the assignment operator overwrites an old object with a copy of another. The assignment operator has to get rid of the old object's stuff, but there is no old object where a constructor is concerned.
Edit:
Is it because the object is newly created when the copy constructor is invoked therefore it receives its own memory address for a pointer such as *name?
Yes. It gets its own name pointer, but it doesn't point to anything -- there's nothing to get rid of.
FWIW, no introductory programming class should even attempt to cover this topic.