i am trying to implement a class with copy and swap idiom but while printing the object i am getting junk value. i am using copy-ctor to make use of copy before i make use of assignment. following is code snippet:
class EmpTasks {
private:
size_t m_size;
char* name;
public:
EmpTasks(char* name, int size = 0)
: m_size(size),
name(m_size ? new char[m_size] : nullptr){
}
EmpTasks(const EmpTasks& source)
: m_size (source.m_size),
name (source.name ? new char [m_size] : nullptr)
{
std::copy(source.name, source.name + m_size, name);
}
//copy assign operator
EmpTasks& operator = (EmpTasks copy_form){
swap(*this, copy_form);
return *this;
}
friend void swap (EmpTasks& first, EmpTasks& second) {
//ADL
using std::swap;
swap(first.m_size, second.m_size);
swap(first.name, second.name);
}
friend ostream& operator<<(ostream& os, const EmpTasks& ep) {
return os << "Employee name: " << ep.name << endl;
}
~EmpTasks()
{
delete[] name;
}
};
here is main snippet. what is it that i am doing wrong.
const int size = 14;
char name[size] = "Some string";
EmpTasks ep (name, size);
cout << ep << endl;
this has occurred because you haven't initialized your var name, hence you can use something like what follows in your ctor
EmpTasks(char* name, int size = 0)
: m_size(size),
name( m_size ? new char[m_size] : nullptr ){
for(size_t i{}; i< m_size; ++i)
this -> name[i] = name[i];
}
Another thing. name[size] = "Some string"; here "Some string" must be < 8 chars
And I think you should consider the following version of copy assignment operator
//copy assign operator
EmpTasks& operator = (const EmpTasks& copy_form){
if(this == & copy_form) return *this;//checks for self-assignment
EmpTasks temp{copy_form};//uses cpy ctor
std::swap(*this, temp);//remember to delete your `swap`
return *this;
}
Because yours is not a copy assignment operator because its parameter is not constant and not taken by reference.
Note that name requires a null terminator '\0'when used with standard library functions like std::operator<<, so the size of it should be at least number of characters + 1.
Related
PART OF MY VECTOR CLASS
class CVector
{
friend ostream &operator<<(ostream& output, const CVector& vec);
public:
CVector()
{
sPtr = new char[10];
size = 0;
capacity = 10;
}
CVector(const CVector &vec)
{
size = vec.size;
capacity = vec.capacity;
sPtr = new char[size];
cout << "copy constructor" << endl;
for (size_t i = 0; i < size; i++)
{
sPtr[i] = vec.sPtr[i];
}
}
Main Function
CVector veca;
cout << veca.getCapacity() << endl;
cout << veca.getSize() << endl;
veca.push_back('H');
veca.push_back('e');
veca.push_back('l');
veca.push_back('l');
veca.push_back('o');
cout << "Add five elements:Hello" << endl << "result:" << veca << " size:" << veca.getSize() << " capacity:" << veca.getCapacity()<<endl;
CVector vecb,vecc;
vecb.push_back('H');
vecb.push_back('i');
vecc = vecb;
After this code(vecc = vecb),the issue happend.I think something went wrong >with copyconstructor but i cannot figure out what's wrong
push_back function
void push_back(char c)
{
if (size == capacity-1)
{
char* temp = new char[2 * capacity];
for (size_t i = 0; i < capacity; i++)
{
temp[i] = sPtr[i];
}
delete[] sPtr;
capacity *= 2;
sPtr = temp;
}
sPtr[size] = c;
size++;
}
Can I assign a *temp local pointer to the *sPtr(pointer to array for my vector)
In your copy constructor, you allocate vec.size bytes, but set capacity to vec.capacity. You probably want to allocate vec.capacity bytes instead.
The next thing is, that there is a difference between copy construction and copy assignment:
CVector vecb(veca); // this calls the copy constructor
CVector vecc; // this calls the default constructor
vecc = vecb; // this calls the copy assignment operator
Since you haven't given an implementation for the copy assignment operator, the compiler will generate one for you, which looks something like that:
CVector & operator = (const CVector & other) {
size = other.size;
capacity = other.capacity;
sPtr = other.sPtr;
}
This is not suitable for you, because
you lose track of sPtr and nobody deletes it.
two different CVectors now point to the same memory via sPtr. When the destructor of the first CVector is called, it will delete sPtr. Then the other CVector will point to memory that is already deleted. When it's destructor (or any other member function) is called, it tries to delete or access that deleted memory.
That is the reason for the rule of 3 (or rule of 5, depending on whether you implement move semantics).
An easy and good way to implement the copy (and move) assignment operator, is to use the copy and swap idiom:
void swap(CVector & other) {
std::swap(size, other.size);
std::swap(capacity, other.capacity);
std::swap(sPtr, other.sPtr);
}
CVector & operator = (const CVector& other) {
CVector tmp(other); // make a copy of other
this->swap(other); // by swapping with other, tmp's destructor, will cleanup your previous sPtr
return *this;
}
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...
The Problem: When I attempt to assign an IntArray object by index I get the following error:
"Expression is not assignable."
The error is produced by the following line of code in iadrv.cpp:
IntArray a(10);
for(int i = a.low(); i <= a.high(); i++)
a[i] = i * 10;
I am able to assign an entire IntArray object to another like so, a = b;, however when a specific index is referred to the "expression is not assignable" error occurs.
EDIT: I removed the const declaration from most of the functions and I am not getting the "Expression is not assignable" error anymore. However, the setName now gives the error:
"ISO C++ 11 does not allow conversion from string literal to 'char *'"
This error is cause by the following code in iadrv.cpp:
a.setName("a");
Program Explanation:
I have written a class IntArray (in C++) in which the following operators are overloaded:
"[ ]" : allows index range checking
"=" : allows array assignment
"+" : allows the sum of two arrays to be assigned to a third array
"+=" : allows the sum of two arrays to be assigned to the first array
"<<" : allows the contents of an array to be output
The program also includes functions:
setName : sets the name of the IntArray object
getName : returns the name of the IntArray object
low : returns the smallest legal index
high : returns the largest legal index
length : returns the number of elements
A driver program (iadrv.cpp, iadrv.h) will run tests on the IntArray class (IntArray.cpp, IntArray.h) to determine if all operators were properly overloaded.
Note: that for each array test data, the driver will simply multiply the
array index by 10 immediately after each array is initialized or modified and output its contents. When the program encounters a run-time error, it should "simulate"a halt with appropriate diagnostics rather than actually halting the program.
The Code:
IntArray.h
// IntArray.h
#ifndef __IntArray__IntArray__
#define __IntArray__IntArray__
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
class IntArray {
private:
int a, b;
int size;
int * num;
char * name;
public:
IntArray(int start, int finish);
IntArray(int finish = 10);
IntArray(const IntArray &); //constructor copy
~IntArray();
int low() const;
int high() const;
char * getName() const;
//removed the const declaration from functions below
int & operator [] (int); //made to return int&
friend ostream & operator << (ostream &, IntArray &);
void setName(char *);
int length() const;
const IntArray & operator = (IntArray &);
const IntArray & operator + (IntArray &);
bool operator += (IntArray &);
};
#endif /* defined(__IntArray__IntArray__) */
IntArray.cpp
// IntArray.cpp
#include "IntArray.h"
#include <iostream>
#include <fstream>
using namespace std;
extern ofstream csis;
IntArray::IntArray(int start, int finish) {
if (start > finish) {
cout << "Simulating a halt.";
a = finish;
b = start;
}
else {
a = start;
b = finish;
}
size = b-a;
num = new int[size];
name = new char[1];
for (int i = 0; i < size; i++) {
num[i] = 0;
}
}
IntArray::IntArray(int finish) {
size = finish;
a = 0;
b = finish - 1;
num = new int[size];
name = new char[1];
for (int i = 0; i < size; i++) {
num[i] = 0;
}
}
IntArray::IntArray (const IntArray & right): size(right.size) {
num = new int[size];
name = new char[1];
for (int i = 0; i < size; i++) {
num[i] = right.num[i];
}
}
IntArray::~IntArray() {
delete[] num;
delete [] name;
}
int IntArray::low() const{
return a;
}
int IntArray::high() const{
return b;
}
char * IntArray::getName() const{
return name;
}
void IntArray::setName(char * n) {
name = n;
}
//removed const declarations
//made to return int&
int & IntArray::operator [] (int subscript) const{
if (subscript < a || subscript > b) {
cout << "subscript: " << subscript << endl;
cout << "Out of bound error. Simulating a halt." << endl;
return num[a];
}
return num[subscript-a];
}
int IntArray::length() const{
//b-a = size
return (b-a);
}
//removed const declarations
ostream & operator << (ostream & output, IntArray & array) {
for (int i = array.low(); i <= array.high(); i++) {
output << array.name << "[" << i << "] = " << array[i] << endl;
}
return output;
}
//removed const declarations
IntArray & IntArray::operator = (IntArray & right) {
if (length() == right.length()) {
for (int i = 0; i <= length(); i++) {
num[i] = right[right.low()+i];
}
return * this;
}
else {
delete [] num; //reclaim space
delete [] name;
size = right.length();
num = new int [size]; //space created
cout << "Different sized arrays. Simulating a hault" << endl;
}
return * this;
}
//removed const declarations
IntArray & IntArray::operator + (IntArray & right) {
int * ptr;
ptr = new int [right.length()];
if (length() == right.length()) {
for (int i = 0; i < length(); i++) {
ptr[i] = num[i] + right[right.low()+i];
}
}
return * this;
}
//removed const declarations
bool IntArray::operator += (IntArray & right) {
if (length() == right.length()) {
for (int i = 0; i <= right.length(); i++) {
num[i] += right[right.low()+i];
}
return true;
}
cout << "Could not add the sum of the arrays into first array. Simulating a halt." << endl;
return false;
}
iadrv.h
// iadrv.h
#ifndef p6_iadrv_h
#define p6_iadrv_h
#include "intarray.h"
int main();
void test1();
void wait();
#endif
iadrv.cpp
// iadrv.cpp
#include <iostream>
#include <iomanip>
#include <fstream>
#include <stdlib.h>
#include "iadrv.h"
using namespace std;
ofstream csis;
int main() {
csis.open("csis.dat");
test1();
csis.close();
}
void test1() {
system("clear");
cout << "1. Array declared with single integer: IntArray a(10);" << endl << endl;
csis << "1. Array declared with single integer: IntArray a(10);" << endl << endl;
IntArray a(10);
for(int i = a.low(); i <= a.high(); i++)
a[i] = i * 10;
a.setName("a");
cout << a << endl;
csis << a << endl;
wait();
}
DISCLAIMER: This program was written as a school assignment and has already been turned in to be graded. This was my first c++ program so I would like to understand my mistakes. Your help is sincerely appreciated.
You have defined your operator[] like this:
const int operator [] (int) const;
that second "const" means that inside that method you cannot modify your object.
So it will only work for getting values, but not for setting them.
Try to remove it and it should work.
EDIT: AS pointed to Bryan Chen, You also need to return a reference and non-const, like this:
int& operator [] (int subscript)
Now, looking more in depth at your code, that is not even enough, because you have this method:
ostream & operator << (ostream & output, const IntArray & array) {
for (int i = array.low(); i <= array.high(); i++) {
output << array.name << "[" << i << "] = " << array[i] << endl;
}
return output;
}
Look that you operator[] needs to work on a non-const IntArray, but in that method your variable "array" is const, so you need to rewrite a bit more of code.
Also, look for the same problem with the rest of the operators, remember: you make a method 'const' only if you don't plan to modify the object from inside that method, and you make a parameter 'const' only if you don't plan to modify that parameter.
Your existing operator does not allow the value to be changed, both because it returns an int by value and because the operator is declared const. You can't assign to a value, only to an object (which includes references, since a reference is just another name for an object).
To accomplish this you need to supplement your existing operator with another, non-const one that returns a reference to the (non-const) int:
int & operator[](int index);
Since this operator would return a reference, you can assign directly to the return value using the familiar a[b] = c syntax you desire to use.
You would not need to change your existing operator, but I would strongly recommend changing the return type from const int to just int -- you are returning by value anyway, so you are handing back a copy. It makes no sense for this to be const, and this may prevent the compiler from eliding copies in the case of more complex data types than int. (It doesn't really make much difference here, but I would avoid getting in the habit of returning both by value and const, since -- assuming the presence of a copy constructor -- the const qualifier can be removed anyway by simply copying the value again. Returning const copies usually provides no benefits while having several drawbacks.)
Since you also asked to point out your mistakes, I would like to comment on two things you should/could have done to make the code more simple:
First, the assignment operator could have been written like this:
IntArray& operator=(IntArray rhs)
{
std::swap(rhs.a, a);
std::swap(rhs.b, b);
std::swap(rhs.size, size);
std::swap(rhs.num, num);
std::swap(rhs.name, name);
return *this;
}
This works, since you have a copy constructor and destructor already defined for IntArray, and they hopefully work correctly. All the assignment operator is doing is creating a temporary object and swapping out its internals with the internals of the current object. Then the temporary dies with the "old data", while the new data is safely in the current object. This is called the copy/swap idiom.
Also note that a reference is returned that is non-const.
If you pass a const reference instead of an object, then the assignment operator is responsible for the initial copy made.
IntArray& operator=(const IntArray& orig)
{
IntArray rhs(orig);
std::swap(rhs.a, a);
std::swap(rhs.b, b);
std::swap(rhs.size, size);
std::swap(rhs.num, num);
std::swap(rhs.name, name);
return *this;
}
The former version may be faster, due to allowing the compiler to optimize the copy of the passed value. However the second form of passing a const reference is what is usually done -- note that the temporary object needs to be created inside the function before proceeding.
Second, your operator + can just use operator +=:
IntArray operator+(const IntArray& rhs)
{
IntArray temp(*this);
return temp += rhs;
}
All we did was create a temporary equal to the current object. Then we use += to add rhs and return the result. Nice and simple. Note that operator + returns a new IntArray object, not a const IntArray. In addition, operator += should return a reference to the current object, not bool.
To take advantage of this, your operator += should be rewritten thusly:
IntArray& operator+=(const IntArray& rhs)
{
//..your current code goes here:
//...
return *this;
}
Also, your operator += should not be "erroring out" like that. You need to make the class more robust by attempting to add two IntArrays that may not be the same size. If there really is an error throw an exception. Don't return a bool -- remove the return true; and return false; from the function altogether. Always return *this.
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.