#include <cassert>
#include <iostream>
using namespace std;
#include <initializer_list>
class IntArray {
unsigned mLength = 0;
int *mData = nullptr;
public:
IntArray(unsigned length) : mLength(length) { mData = new int[length]; }
~IntArray() { delete[] this->mData; }
IntArray(const std::initializer_list<int> &list) : IntArray(list.size()) {
int count = 0;
for (auto &e : list) {
mData[count] = e;
++count;
}
}
friend ostream &operator<<(ostream &os, IntArray &arr) {
for (unsigned i = 0; i < arr.mLength; ++i) {
os << arr.mData[i] << " ";
}
os << endl;
return os;
}
//=operator
IntArray &operator=(const std::initializer_list<int> &array) {
delete[] mData;
mLength = array.size();
if (mData != nullptr) {
// mData = new int[mLength+1];
cout << "mLength is " << mLength << endl;
mData = new int[mLength];
int i{};
for (auto &e : array) {
mData[i++] = e;
// There's a buffer overrun due to this line
// so i change
// mData = new int[mLength]; to mData = new int[mLength+1];
// Is this bad?
// Let me know if there's another better way.
}
} else {
mData = nullptr;
}
return *this;
}
};
int main() {
IntArray intArray = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << intArray << endl;
}
buffer overrun problem keep bodering me.. coding masters
mData[i++] = e;
There's a buffer overrun due to this line
It's more than the index of the array, right?
so i change
this linemData = new int[mLength];
to mData = new int[mLength+1];
Is this bad?
Let me know if there's another better way.
plzplz
coding is so hard...
r u guys are all genius??
Your code runs perfectly on clang, gcc and msvc. There is no issue with what you're referring to whatsoever, however, I want to point out to something in the IntArray &operator=(const std::initializer_list<int> &array);, the first line of this method is delete[] mData; which may cause some undefined behavior if mData was nullptr, so change it to if(mData) delete[] mData;, the else{} statement doesn't run so I'd suggest you change change it to something like this
IntArray &operator=(const std::initializer_list<int> &array) {
if(mData) delete[] mData;
mLength = array.size();
cout << "mLength is " << mLength << endl;
mData = new int[mLength];
int i{};
for (auto &e : array) {
mData[i++] = e;
}
return *this;
}
This is a possible implementation of it
Related
I want to make "set" function. This function changes the values of a2 from "set(int x, int y)" to "y"
#include <iostream>
using namespace std;
class IntArray {
private:
int* m_data;
int m_len;
public:
IntArray(int = 0, int = 0);
~IntArray();
void print(void);
IntArray(const IntArray& copy); // copy Constructor
void set(int x , int y) {
int temp = x;
x = y;
y = temp;//!!
}
};
IntArray::IntArray(int size, int init) {
if (size <= 0) {
m_data = nullptr; m_len = 0;
}
else {
m_data = new int[size];
m_len = size;
for (int idx = 0; idx < m_len; ++idx)
*(m_data + idx) = init;
}
}
IntArray::~IntArray() {
delete[]m_data;
}
void IntArray::print(void) {
for (int idx = 0; idx < m_len; ++idx)
cout << *(m_data + idx) << ' ';
cout << std::endl;
}
int main() {
cout << "a1: ";
IntArray a1{ 10, 100 };
a1.print();
cout << "a2: ";
IntArray a2{ a1 };// 10~100
a2.set(3, 999);
a2.set(9, 123);
a2.print();
return 0;
}
Excepted
a1: 100 100 100 100 100 100 100 100 100 100
a2: 100 100 100 999 100 100 100 100 100 123
Here's what you need.
let's fix up your declaration and introduce two new helper methods, alloc and copyFromOther.
class IntArray {
private:
int* m_data;
int m_len;
void alloc(int len);
void copyFromOther(const IntArray& copy);
public:
IntArray();
IntArray(const IntArray& copy);
~IntArray();
IntArray& operator=(const IntArray& copy);
void print();
void set(int index, int value);
};
Then the helper methods are for quick allocations and copy. Notice that I didn't special case length == 0. That's because it's perfectly fine to say, new int[0]. It will allocate a pointer of zero length. You can change this if you want to force a null pointer when length is zero. Also be aware that copy constructors need to be able to handle "copying to yourself". That is, if you say:
IntArray arr(10, 20);
arr.set(5,5);
arr = arr;
As weird as the above looks, it's the source of a lot of bugs when a copy constructor doesn't handle that case. That's covered below.
So now the implementation
void IntArray::alloc(int len) {
delete [] m_data; // erase what we had before
m_data = new int[len]; // you could say m_data = len ? new int[len] : nullptr
m_len = len;
}
void IntArray::copyFromOther(const IntArray& copy) {
// special case - allow for "copying to ourselves" by doing nothing
// if we didn't handle this, the alloc function would delete the pointer
if (copy.m_data == m_data) {
return;
}
alloc(copy.m_len);
for (int idx = 0; idx < m_len; ++idx)
m_data[idx] = copy.m_data[idx];
}
}
Then the implementation
IntArray::IntArray() : m_data(nullptr) {
alloc(0);
}
IntArray::IntArray(const IntArray& copy) m_data(nullptr), m_len(0) {
copyFromOther(copy);
}
IntArray& IntArray::operator=(const IntArray& copy) {
copyFromOther(copy);
return *this;
}
And let's fix your set method as you had asked about it in a separate question:
void IntArray::set(int index, int value) {
if ((index >= 0) && (index < m_len)) {
m_data[index] = value;
}
}
Your destructor and print methods are fine as you have them. print doesn't need to have an explicit void parameter.
I'm currently building a library In C++. I have met this problem few days ago and I'm unable to fix it. I have shorten the code so it can be seen easier.
Below is my code:
class String
{
private:
mutable char* v;
mutable int l = 0;
public:
String()
{
l++;
v = new char[1];
*v = '\0';
}
String(const char* value)
{
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
String(const String& value)
{
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
int len() const
{
return l - 1;
}
char* val() const
{
return v;
}
char* operator=(const char* value) const
{
delete[] v;
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char* operator=(const String& value) const
{
delete[] v;
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char operator[](const int& index) const
{
return v[index];
}
};
class StringArray
{
private:
union ArrayDef
{
public:
mutable String stringV;
mutable int intV;
ArrayDef()
{
}
ArrayDef(const String& value)
: stringV(value)
{
}
ArrayDef(const int& value)
: intV(value)
{
}
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
String operator=(const String& value) const
{
stringV = value;
return stringV;
}
int operator=(const int& value) const
{
intV = value;
return intV;
}
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
};
mutable ArrayDef* arrdef;
mutable int arrLen = 0;
public:
StringArray()
{
}
void add(const ArrayDef& value) const
{
ArrayDef temp[arrLen + 1];
for (int i = 0; i < arrLen; i++)
temp[i] = arrdef[i];
temp[arrLen] = value;
arrLen++;
delete[] arrdef;
arrdef = new ArrayDef[arrLen];
for (int i = 0; i < arrLen; i++)
arrdef[i] = temp[i];
}
int len() const
{
return arrLen;
}
ArrayDef val(const int& index) const
{
return arrdef[index];
}
};
And my driver code:
#include <iostream>
int main()
{
StringArray arr;
arr.add(String("Hello"));
arr.add(String("World"));
std::cout << "Length of the array: " << arr.len() << std::endl;
int indexOfString = 1;
int indexOfCharacter = 2;
char s = arr.val(indexOfString).stringV[indexOfCharacter];
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have created two class, that is, String and StringArray class.
For String class, I need to always add a null character after the char pointer array for safety issue.
For StringArray class, I uses union because it's actually an array for multiple types.
It can be successfully compiled but it output some random character and it is different every time I run it.
Any answers will be appreciated, and please tell me why and how it don't works. Thank you.
From,
HaiQin.
This code is just a collection of antipatters that makes it difficult to study. What is the reason of making the internal data mutable? Why do you need to play with length and l where sometimes it is the length of the string, sometimes it is the size of array? The operator operator= returns char* which is a bad practice. Using const int& index as a parameter is a strange choice. You allocate arrays multiple times but you have no destructor that frees the memory.
Here your assignment operator returns a value, not reference!
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
Next comes even more dangerous practice:
// Recollect that this is a union
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
You are assigning both fields of the union at the same time! Did you mean struct?
Try to fix that. Start with changing union to structure.
One of the things that certainly will not work is the ArrayDef copy constructor and operator=(const ArrayDef & value). This is because you may only use the active value in the union, not both at the same time. This is usually solved by using a tagged union. Is there a reason you cannot use the Standard Template Library?
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> arr;
arr.push_back(std::string("Hello"));
arr.push_back(std::string("World"));
std::cout << "Length of the array: " << arr.size() << std::endl;
constexpr int indexOfString = 1; // second string - starting from 0!
constexpr int indexOfCharacter = 2; // third character
char s = arr.at(indexOfString).c_str()[indexOfCharacter]; // using interfaces closest to the original
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I am receiving a runtime error from code I have written. When I call the grow() function it works perfectly until it hits the delete, then it crashes and says:
This really confuses me and I do not understand what is going on with that.
Here is my code where it is happening:
The .cpp File
#include "MyVector.h"
#include <cstdlib>;
void MyVector::grow()
{
if (cap == 0)
cap = MINCAP;
else
cap = cap*MINCAP;
int* temp = new int[cap];
for (int i = 0; i < vectorSize; i++)
{
temp [i] = theVector[i];
}
for (int i = vectorSize; i < cap; i++)
{
temp[i] = 0;
}
delete[] theVector;
theVector = temp;
}
MyVector::MyVector()
{
clear();
}
MyVector::~MyVector()
{
delete[] this->theVector;
}
MyVector::MyVector(int _cap)
{
cap = _cap;
for (int i = 0; i < cap; i++)
{
this->theVector[i] = 0;
}
}
MyVector::MyVector(const MyVector & vect)
{
this->vectorSize = vect.vectorSize;
this->cap = vect.cap;
delete[] theVector;
theVector = new int[cap];
for (int i = 0; i < vectorSize; i++)
{
this->theVector[i] = vect.theVector[i];
}
for (int i = vectorSize; i < cap; i++)
{
this->theVector[i] = 0;
}
}
int MyVector::size() const
{
return vectorSize;
}
int MyVector::capacity() const
{
return cap;
}
void MyVector::clear()
{
vectorSize = 0;
cap = MINCAP;
delete[] theVector;
theVector = new int[MINCAP];
for (int i = 0; i <MINCAP; i++)
{
*(theVector + i) = 0;
}
}
//Put an int into the vector
void MyVector::push_back(int n)
{
if (theVector == nullptr)
clear();
if (vectorSize+1 >= cap)
{
grow();
theVector[vectorSize] = n;
vectorSize++;
}
else
{
theVector[vectorSize] = n;
vectorSize++;
}
}
int MyVector::at(int _location)
{
if (_location < 0 || _location >= vectorSize)
throw _location;
return theVector[_location];
}
MyVector & MyVector::operator=(const MyVector & rho)
{
// test for self assignment
if (this == &rho)
return *this;
// clean up array in left hand object (this)
delete[] this->theVector;
// create a new array big enough to hold right hand object's data
this->vectorSize = rho.size();
this->cap = rho.cap;
this->theVector = new int[cap];
// copy the data
for (int i = 0; i < vectorSize; i++)
{
this->theVector[i] = rho.theVector[i];
}
for (int i = vectorSize; i < cap; i++)
{
this->theVector[i] = 0;
}
// return this object
return *this;
}
The.h File
#pragma once
#include <array>
#include <fstream>
using namespace std;
class MyVector
{
private:
//the minimum capacity of the vector
const int MINCAP = 2;
//the amount of items in the vector
int vectorSize = 0;
//the maximum ammount of items in the vector
int cap = MINCAP;
//The pointer to the first integer in the vector
int* theVector = new int[MINCAP];
//The grow function
//Parameters : none
//returns : none
void grow();
public:
//The nonparmeterized constructor
//Parameters : none
//returns :none
MyVector();
//The deconstructor
//Parameters : none
//returns : none
~MyVector();
// The parameterized constructor
//Parameters : the capacity to set it to
//returns : none
MyVector(int _cap);
//Get the size
//Parameters : none
//returns : the size
MyVector(const MyVector& vect);
//Get the size
//Parameters : none
//returns : the size
int size() const;
//get the capacity
//Parameters : none
//returns : cap
int capacity() const;
//clear the data
//Parameters : none
//returns :none
void clear();
//insert an int into the vector
//Parameters : the int
//returns : none
void push_back(int n);
//gets the int at a location
//Parameters : the location
//returns : the integer
int at(int _location);
//Overload the = operator
//Parameters : the one to copy
//returns : the vector
MyVector& operator=(const MyVector& rho);
//Overload the << operator
//Parameters : the one to copy
//returns : the vector
friend ostream& operator<<(ostream& os, MyVector& TheVector)
{
for (int i = 0; i < TheVector.size(); i++)
{
os << TheVector.at(i) << ", ";
}
return os;
}
};
The driver to test it.
#include <iostream>
#include "MyVector.h"
using namespace std;
// the printV function
// used to test the copy constructor
// parameter: a MyVector object
void printV(MyVector);
int main()
{
cout << "\nCreating a vector Sam of size 4.";
MyVector sam(4);
cout << "\nPush 12 values into the vector.";
for (int i = 0; i < 12; i++)
sam.push_back(i);
cout << "\nHere is sam: ";
cout << sam;
cout << "\n---------------\n";
cout << "\nCreating a vector Joe of size 4.";
MyVector joe(4);
cout << "\nPush 6 values into the vector.";
for (int i = 0; i < 6; i++)
joe.push_back(i * 3);
cout << "\nHere is joe: ";
cout << joe;
cout << "\n---------------\n";
cout << "\nTest the overloaded assignment operator \"joe = sam\": ";
joe = sam;
cout << "\nHere is sam: ";
cout << sam;
cout << "\n---------------\n";
cout << "\nHere is joe: ";
cout << joe;
cout << "\n---------------\n";
// pass a copy of sam by value
printV(sam);
cout << endl;
system("PAUSE");
return 0;
}
void printV(MyVector v)
{
cout << "\n--------------------\n";
cout << "Printing a copy of a vector\n";
cout << v;
}
EDIT: I updated the code with the recommendations.
There are a few problems with your code:
your MyVector(int) constructor does not allocate the requested number of array elements. It is not even touching theVector at all, so theVector is being member-initialized to the default of MINCAP elements, even if the requested cap is actually higher.
your MyVector(MyVector&) copy constructor is declared wrong. To be a proper copy constructor, it needs to take the input object by const reference instead. Without that, you do not have a proper copy constructor. If the compiler generates its own default copy constructor, it will simply copy your theVector pointer from one object to another and not actually make a new copy of the array data, thus causing ownership problems.
case in point, your printV() function takes an input MyVector by value, which copy-constructs a temporary object. When the temporary goes out of scope and calls delete[] on its theVector pointer, it is actually going to destroy the array in the original MyVector object if the compiler's default copy constructor is used. You need a proper copy constructor.
clear() is using delete instead of delete[]. Memory allocated with new must be freed with delete. Memory allocated with new[] must be freed with delete[]. Mismatching them can cause memory problems.
With that said, I would suggest the following implementation:
#pragma once
#include <iostream>
class MyVector
{
private:
//the minimum capacity of the vector
const int MINCAP = 2;
//the number of items in the vector
int vectorSize = 0;
//the maximum number of items in the vector
int cap = 0;
//The pointer to the first integer in the vector
int* theVector = nullptr;
//The grow function
//Parameters : none
//returns : none
void grow();
public:
//The nonparmeterized constructor
//Parameters : none
//returns :none
MyVector();
// The parameterized constructor
//Parameters : the capacity to set it to
//returns : none
MyVector(int _cap);
// The copy constructor
//Parameters : the vector to copy from
//returns : none
MyVector(const MyVector& src);
// The move constructor
//Parameters : the vector to move from
//returns : none
MyVector(MyVector&& src);
//The destructor
//Parameters : none
//returns : none
~MyVector();
//Get the size
//Parameters : none
//returns : the size
int size() const;
//get the capacity
//Parameters : none
//returns : cap
int capacity() const;
//clear the data
//Parameters : none
//returns :none
void clear();
//insert an int into the vector
//Parameters : the int
//returns : none
void push_back(int n);
//gets the int at a location
//Parameters : the location
//returns : the integer
int at(int _location);
//swap the content of a vector with another
//Parameters : the vector to swap with
//returns : none
void swap(MyVector& other);
//Overload the copy = operator
//Parameters : the one to copy from
//returns : the vector
MyVector& operator=(const MyVector &rho);
//Overload the move = operator
//Parameters : the one to move from
//returns : the vector
MyVector& operator=(MyVector && rho);
//Overload the << operator
//Parameters : the one to print
//returns : the stream
friend std::ostream& operator<<(std::ostream& os, const MyVector& rho);
};
std::ostream& operator<<(std::ostream& os, const MyVector& rho);
void swap(MyVector &v1, MyVector &v2) { v1.swap(v2); }
namespace std {
template <>
void swap(MyVector &v1, MyVector &v2)
{
v1.swap(v2);
}
}
#include "MyVector.h"
#include <algorithm>
#include <utility>
MyVector::MyVector()
: MyVector(MINCAP)
{
}
MyVector::MyVector(int _cap)
: theVector(new int[_cap]), cap(_cap)
{
std::fill_n(theVector, cap, 0);
}
MyVector::MyVector(const MyVector & src)
: theVector(new int[src.cap]), cap(src.cap), vectorSize(src.vectorSize)
{
std::copy_n(src.theVector, vectorSize, theVector);
std::fill_n(theVector+vectorSize, cap-vectorSize, 0);
}
MyVector::MyVector(MyVector && src)
{
src.swap(*this);
}
MyVector::~MyVector()
{
delete[] theVector;
}
void MyVector::grow()
{
int newcap = cap;
if (newcap == 0)
newcap = MINCAP;
else
newcap = newcap * MINCAP;
int* newVector = new int[newcap];
std::copy_n(theVector, vectorSize, newVector);
std::fill_n(newVector+vectorSize, newcap-vectorSize, 0);
delete[] theVector;
theVector = newVector;
cap = newcap;
}
int MyVector::size() const
{
return vectorSize;
}
int MyVector::capacity() const
{
return cap;
}
void MyVector::clear()
{
*this = MyVector();
}
void MyVector::push_back(int n)
{
if (vectorSize >= cap)
grow();
theVector[vectorSize] = n;
++vectorSize;
}
int MyVector::at(int _location)
{
if ((_location < 0) || (_location >= vectorSize))
throw _location;
return theVector[_location];
}
void MyVector::swap(MyVector& other)
{
std::swap(theVector, other.theVector);
std::swap(cap, other.cap);
std::swap(vectorSize, other.vectorSize);
}
MyVector& MyVector::operator=(const MyVector &rho)
{
if (this != &rho)
{
int newcap = rho.cap;
if (cap != newcap)
{
delete[] theVector;
theVector = nullptr;
cap = 0;
vectorSize = 0;
theVector = new int[newcap];
cap = newcap;
}
int newsize = rho.vectorSize;
std::copy_n(rho.theVector, newsize, theVector);
std::fill_n(theVector+newsize, cap-newsize, 0);
vectorSize = newsize;
}
return *this;
}
MyVector& MyVector::operator=(MyVector && rho)
{
rho.swap(*this);
return *this;
}
std::ostream& operator<<(std::ostream& os, const MyVector& rho)
{
os << "size " << rho.vectorSize << ", capacity " << rho.cap;
if (rho.vectorSize > 0)
{
os << "\n" << rho.theVector[0];
for (int i = 1; i < rho.vectorSize; ++i)
{
os << ", " << rho.theVector[i];
}
}
return os;
}
#include <iostream>
#include "MyVector.h"
// the printV function
// used to test the copy constructor
// parameter: a MyVector object
void printV(MyVector);
int main()
{
std::cout << "\nCreating a vector Sam of capacity 4.";
MyVector sam(4);
std::cout << "\nPush 12 values into the vector.";
for (int i = 0; i < 12; ++i)
sam.push_back(i);
std::cout << "\nHere is sam: ";
std::cout << sam;
std::cout << "\n---------------\n";
std::cout << "\nCreating a vector Joe of capacity 4.";
MyVector joe(4);
std::cout << "\nPush 6 values into the vector.";
for (int i = 0; i < 6; ++i)
joe.push_back(i * 3);
std::cout << "\nHere is joe: ";
std::cout << joe;
std::cout << "\n---------------\n";
std::cout << "\nTest the overloaded copy assignment operator \"joe = sam\": ";
joe = sam;
std::cout << "\nHere is sam: ";
std::cout << sam;
std::cout << "\n---------------\n";
std::cout << "\nHere is joe: ";
std::cout << joe;
std::cout << "\n---------------\n";
std::cout << "\nTest the overloaded move assignment operator \"joe = MyVector(5)\": ";
joe = MyVector(5);
std::cout << "\nHere is joe: ";
std::cout << joe;
std::cout << "\n---------------\n";
// pass a copy of sam by value
printV(sam);
std::cout << std::endl;
system("PAUSE");
return 0;
}
void printV(MyVector v)
{
cout << "\n--------------------\n";
cout << "Printing a copy of a vector\n";
cout << v;
}
I am not sure what I am doing wrong here but I used Google and found almost the identical code to mine but they did not claim an error. Our assignment is to overload the insertion operator. The problem is that I get an error in the driver for every time I pass SAM or JOE. The error states: Error 2 error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'MyVector' (or there is no acceptable conversion).
Any help is appreciated. Please note that I have not completed this assignment and if another function doesn't seem right, it may be because I haven't work on it.
Thanks in advance.
Driver.cpp
#include <iostream>
#include "MyVector.h"
using namespace std;
// the printV function
// used to test the copy constructor
// parameter: a MyVector object
void printV(MyVector);
int main()
{
cout << "\nCreating a vector Sam of size 4.";
MyVector sam(4);
cout << "\nPush 12 values into the vector.";
for (int i = 0; i < 12; i++)
sam.push_back(i);
cout << "\nHere is sam: ";
cout << sam;
cout << "\n---------------\n";
cout << "\nCreating a vector Joe of size 4.";
MyVector joe(4);
cout << "\nPush 6 values into the vector.";
for (int i = 0; i < 6; i++)
joe.push_back(i * 3);
cout << "\nHere is joe: ";
cout << joe;
cout << "\n---------------\n";
cout << "\nTest the overloaded assignment operator \"joe = sam\": ";
joe = sam;
cout << "\nHere is sam: ";
cout << sam;
cout << "\n---------------\n";
cout << "\nHere is joe: ";
cout << joe;
cout << "\n---------------\n";
// pass a copy of sam by value
printV(sam);
cout << endl;
system("PAUSE");
return 0;
}
void printV(MyVector v)
{
cout << "\n--------------------\n";
cout << "Printing a copy of a vector\n";
cout << v;
}
My Vector.h
#pragma once
class MyVector
{
private:
int vSize;
int vCapacity;
int* vArray;
void grow();
public:
MyVector();
MyVector(int n);
MyVector(const MyVector& b);
int size() const;
int capacity() const;
void clear();
void push_back(int n);
int& at(int n) const;
MyVector& operator=(const MyVector& rho);
~MyVector();
};
MyVector.cpp
#include "MyVector.h"
#include <iostream>
using namespace std;
MyVector::MyVector()
{
vArray = nullptr;
vSize = 0;
vCapacity = 0;
}
MyVector::MyVector(int n)
{
vArray = new int[vCapacity];
vSize = 0;
vCapacity = n;
}
MyVector::MyVector(const MyVector& b)
{
vSize = b.size();
vArray = new int[vSize];
for (int i = 0; i < b.size(); i++)
{
this->vArray[i] = b.vArray[i];
}
}
int MyVector::size() const
{
return vSize;
}
int MyVector::capacity() const
{
return vCapacity;
}
void MyVector::clear(void)
{
if (vArray != nullptr)
{
delete[] vArray;
vArray = nullptr;
vSize = 0;
vCapacity = 0;
}
}
void MyVector::push_back(int n)
{
if (vCapacity == 0)
{
vCapacity++;
int* tmp = new int[vCapacity];
delete[] vArray;
vArray = tmp;
}
if (vSize >= vCapacity)
{
grow();
}
vArray[vSize] = n;
vSize++;
}
void MyVector::grow()
{
vCapacity = vCapacity + vCapacity;
int* tmp = new int[vCapacity];
for (int i = 0; i < vSize; i++)
{
tmp[i] = vArray[i];
}
delete[] vArray;
vArray = tmp;
}
int& MyVector::at(int index) const
{
if (index >= 0 && index<vSize)
{
return vArray[index];
}
else
{
throw index;
}
}
MyVector& MyVector::operator=(const MyVector& rho)
{
// test for self assignment
if (this == &rho)
return *this;
// clean up array in left hand object (this)
delete[] this->vArray;
// create a new array big enough to hold right hand object's data
vSize = rho.size();
this->vArray = new int[vSize];
// copy the data
for (int i = 0; i < rho.size(); i++)
{
this->vArray[i] = rho.vArray[i];
}
// return this object
return *this;
}
MyVector::~MyVector()
{
if (vArray != nullptr)
{
clear();
}
}
ostream& operator<<(ostream& out, const MyVector& rho)
{
for (int i = 0; i < rho.size(); i++)
{
out << rho.at(i);
}
return out;
}
Declare std::ostream& operator<<(std::ostream& out, const MyVector& rho) in My Vector.h. That function is used in Driver.cpp, therefore Driver.cpp must know about it.
If you do this, you should also #include <ostream> in My Vector.h.
To be able to use your offending operator<<() in main(), there needs to be a declaration of it visible to the compiler when compiling Driver.cpp.
That means you need to declare your operator<<() in MyVector.h.
Having the definition of operator<<() in MyVector.cpp is fine, but that is not visible to the compiler when compiling Driver.cpp
I think my destructor is good now... but still not sure how to call print_set from within the operator() overload.
It outputs as it should, but I feel like there is an easy way to just call print_set from within the function call overload... can't seem to get things how I want them..
Header file:
/**
* disjoint_sets class declaration
*
*/
#include<ostream>
#include<string>
#include<list>
class disjoint_sets
{
public:
disjoint_sets(int n); //n is the maximum int that can be in a set, so arrays need to be of size n+1
~disjoint_sets();
bool make_set(int n);
int find_set(int u);
bool union_set(int u, int v);
void print_set(std::ostream& out, int u); //e.g. { 1, 2, 4, 8, 10, }
operator std::string(); //e.g. "{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }"
private:
typedef std::list<int>* listptr;
int* p; //parent array
int* rank; //rank array
listptr* set; //array of pointers. Each set[i] is a pointer is to a list<int> containing the integers in the i'th set
int size; //the size i.e. maximum int that can be in a set
};
Implementation:
/**
* disjoint_sets class implementation
*
*/
#include <iostream>
#include <istream>
#include <sstream>
#include <string>
#include <list>
#include "disjoint.hpp"
/*********************
* CONSTRUCTOR
*********************/
disjoint_sets::disjoint_sets(int n)
{
size = n;
// initialize all arrays to same size (n+1)
p = new int[n+1];
rank = new int[n+1];
set = new listptr[n+1];
// loop through set (array of pointers) and initialize
// each element to an empty list
for (int i(1); i <= n; i++)
{
set[i] = new std::list<int>;
}
}
/*********************
* DESTRUCTOR
*********************/
disjoint_sets::~disjoint_sets()
{
delete[] p;
delete[] rank;
// delete each element in the set first, then delete the set.
for(int i(1); i<size+1; i++)
{
set[i]->clear();
}
delete[] set;
}
/*********************
* MAKE_SET
*********************/
bool disjoint_sets::make_set(int n)
{
// if parent already exists
if (p[n] != 0)
{
return false;
}
// else we need to make a set
else
{
// make itself the parent
p[n] = n;
// use push_front to add the int to front of set
set[n]->push_front(n);
return true;
}
}
/*********************
* FIND_SET
***********************/
int disjoint_sets::find_set(int u)
{
while (u != p[u])
{
u = p[u];
}
// returns parent
return u;
}
/*********************
* UNION_SET
*********************/
bool disjoint_sets::union_set(int u, int v)
{
// assign parents to u, v
u = find_set(u);
v = find_set(v);
// return false if parent is 0, list is empty
if (u == 0 or v == 0)
{
return false;
}
// if u's height is less than v's (not in same set)
if (rank[u] < rank[v])
{
// point u's parent to v (merge them)
p[u] = v;
// splice u out
(*set[v]).splice((*set[v]).end(), (*set[u]));
return true;
}
// u's height is greater than or equal to v's height
else
{
// set v's parent to u
p[v] = u;
// splice v out
(*set[u]).splice((*set[u]).end(), (*set[v]));
return true;
}
// if ranks are equal
if (rank[u] == rank[v])
{
// increment rank of u
rank[u]++;
return true;
}
}
/*********************
* PRINT_SET
*********************/
void disjoint_sets::print_set(std::ostream& out, int u)
{
// begin brace for set
out << "{ ";
// loop through with iter, seperate with comma
for (std::list<int>::iterator iter((*set[u]).begin()); iter != (*set[u]).end(); iter++)
{
out << *iter << ", ";
}
// end brace for set
out << "}";
}
/*********************
* STRING CONVERSION
*********************/
disjoint_sets::operator std::string()
{
// sstream variable
std::stringstream text;
// pointer to int array
int *test;
test = new int[size+1];
// opening paren
text << "{ ";
// start main loop through the array
for (int i(1); i <= (size + 1); i++)
{
// if the root is empty
if (test[find_set(i)] == 0)
{
// set to anything != 0?
test[find_set(i)] = 10;
// check if list is empty
if (set[i]->empty())
{
text << "";
}
else
{
// individual set opening parenthesis
text << "{ ";
// use iterator to loop through and load up the stringstream, separate w/ comma
for (std::list<int>::iterator iter((*set[i]).begin()); iter != (*set[i]).end(); iter++)
{
text << *iter << ", ";
}
// individual set closing parenthesis w/ proper punctuation
text << "}, ";
}
}
}
// closing parenthesis
text << "}";
delete[] test;
return text.str();
}
Driver:
#include<iostream>
#include "disjoint.hpp"
int main()
{
using namespace std;
disjoint_sets ds(12);
for( int i=1; i <= 10; ++i)
ds.make_set(i);
ds.union_set(1,2);
ds.union_set(2,4);
ds.union_set(1,8);
ds.union_set(3,6);
ds.union_set(2,10);
//{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }
cout << string(ds) << endl;
}
Just focusing on the destructor:
// delete[] set; // need special conditions for this?
Yes, you need to delete each element in the set first and then delete the set. Use std::shared_ptr if you are not interested to do the memory management.
As a hint: You might get better answers if you ask concrete questions.