So I'm trying to write a vector class which can take objects without a default constructor. To do so I'm using an array of raw pointers. Thing is, when I'm instantiating a new vector, let's call it 2, based upon a previous vector, let's call it 1-- it points to the underlying address of said previous object, 1. So when I insert a new value into 1, 2 is updated as well.
#ifndef MYVECTOR_MYVECTOR_H
#define MYVECTOR_MYVECTOR_H
#include <sstream>
#include <stdexcept>
#include <memory>
#include <vector>
#include <ostream>
template<typename T>
class MyVector;
template<typename T>
std::ostream& operator<<(std::ostream& out, const MyVector<T>& myVec){
out << "{";
for(int i = 0; i < myVec.numElem; i++){
out << &myVec.elements[i] << " ";
}
out << "}";
return out;
}
template<typename T>
class MyVector{
public:
int numElem;
int capacity;
T** elements;
MyVector(const unsigned int& numElements, const T& value) : numElem(numElements), capacity(numElements * 2){
elements = new T*[capacity];
for(int i = 0; i < numElem; i++){
elements[i] = new T(value);
}
}
template<typename U>
MyVector(const std::vector<U>& values): numElem(values.size()), capacity(values.size() * 2){
elements = new T*[capacity];
for(int i = 0; i < numElem; i++){
elements[i] = new T(values[i]);
}
}
void insert(const unsigned& pos, const T& value){
elements[numElem] = new T(*elements[numElem - 1]);
numElem++;
for(unsigned int i = numElem - 1; i > pos; i--){
elements[i] = elements[i - 1];
}
elements[pos] = new T(value);
}
};
#endif
Per comment #1:
class NoDefault {
public:
NoDefault(const int& value) : value(value) {}
int value;
};
std::ostream& operator<<(std::ostream& out, const NoDefault& noDefault) {
out << noDefault.value;
return out;
}
int main() {
MyVector<NoDefault> noDefaultVec(std::vector<NoDefault>{7,8,9,10,11});
MyVector<MyVector<NoDefault>> vecvec(2, noDefaultVec);
std::cout << "noDefaultVec = " << noDefaultVec << std::endl;
std::cout << "vecvec = " << vecvec << std::endl;
noDefaultVec.insert(3, 99);
vecvec.insert(1, noDefaultVec);
std::cout << "noDefaultVec = " << noDefaultVec << std::endl;
std::cout << "vecvec = " << vecvec << std::endl;
return 0;
}
You perform a shallow copy instead of a deep copy.
A shallow copy of your vector creates a new collection which shares elements with an old one. What follows, any change made to the original object will cause a change in a new one. What you need in that case is a deep copy, which duplicates every element from the source to the destination. After performing such a copy you are left with two vectors with seperate set of data.
Providing a copy constructor for your class solves the problem, but you should also remember about implementing destructor and assignment operator, basing on The Rule Of Three. You can also consider adding move constructor and move assignment operator (or one assignment operator following copy-and-swap idiom).
Related
I've been having a hell of a time trying to solve this. What I am trying to do is use operator overloading so that my objects behave more like a multi dimensional array. I've found solutions to several of the smaller problems involved in making this happen but whenever I try to put it all together there is one issue or another, either lvalue assignment error or invalid initialization from rvalue or just straight up seg fault. I would appreciate any advice TY.
#include <iostream>
#include <vector>
#include <string>
class Matrix {
std::string **m;
public:
Matrix(int x, int y) {
m = new std::string*[x];
for (int i = 0; i < x; i++)
m[x] = new std::string[y];
}
class Proxy {
std::string *mm;
int lastIndex = 0;
public:
Proxy(std::string *s) : mm(s) {}
std::string &operator[](int index) {
lastIndex = index;
return mm[index];
}
std::string &operator=(std::string s) {
mm[lastIndex] = s;
return mm[lastIndex];
}
};
Proxy operator[](int index) {
return Proxy(m[index]);
}
};
int main()
{
Matrix *m = new Matrix(5, 5);
m[2][2] = std::string("It Works");
std::cout << m[2][2] << std::endl;
return 0;
In main(), m is a pointer to a Matrix object, so you need to dereference the pointer in order to access the Matrix object so you can invoke your Matrix::operator[] on it, eg:
int main()
{
Matrix *m = new Matrix(5, 5);
(*m)[2][2] = "It Works";
std::cout << (*m)[2][2] << std::endl;
delete m;
return 0;
}
Online Demo
Otherwise, the pointer is not really needed in your example to begin with, eg:
int main()
{
Matrix m(5, 5);
m[2][2] = "It Works";
std::cout << m[2][2] << std::endl;
return 0;
}
Online Demo
Either way, your Proxy does not need to implement operator= at all, eg:
class Proxy {
std::string *mm;
public:
Proxy(std::string *s) : mm(s) {}
std::string& operator[](int index) {
return mm[index];
}
};
A statement like m[2][2] = "..."; will not invoke your Proxy::operator=, it will invoke only Proxy::operator[]. A statement like m[2] = "..."; would be needed to invoke Proxy::operator=, which doesn't make sense to do in a multi-dimensional scenario.
Also, your Matrix constructor has a bug - writing to m[x] is going out of bounds of the m[] array, so the array is not actually filled at all, and you are corrupting surrounding memory, and leaking memory. You need to write to m[i] instead:
//m[x] = new std::string[y];
m[i] = new std::string[y];
After fixing that, Matrix is still leaking memory, as it does not implement a destructor to free the std::strings. You must delete[] anything you new[] (same with delete and new).
And then, you should finish off implementing support for the Rule of 3/5/0, by implementing a copy constructor and a copy assignment operator (your example code does not need them, but production code should always have them), eg:
#include <iostream>
#include <string>
#include <utility>
class Matrix {
std::string **m;
int m_x, m_y;
public:
Matrix(int x = 0, int y = 0) : m_x(x), m_y(y) {
m = new std::string*[x];
for (int i = 0; i < x; ++i)
m[i] = new std::string[y];
}
Matrix(const Matrix &src) : m_x(src.m_x), m_y(src.m_y) {
m = new std::string*[m_x];
for (int i = 0; i < m_x; ++i) {
m[i] = new std::string[m_y];
for (int j = 0; j < m_y; ++j) {
m[i][j] = src.m[i][j];
}
}
}
~Matrix() {
for (int i = 0; i < m_x; ++i)
delete[] m[i];
delete[] m;
}
Matrix& operator=(const Matrix &rhs) {
if (&rhs != this) {
Matrix temp(rhs);
std::swap(m, temp.m);
std::swap(m_x, temp.m_x);
std::swap(m_y, temp.m_y);
}
return *this;
}
class Proxy {
std::string *mm;
public:
Proxy(std::string *s) : mm(s) {}
std::string& operator[](int index) {
return mm[index];
}
};
Proxy operator[](int index) {
return Proxy(m[index]);
}
};
int main()
{
Matrix m(5, 5);
m[2][2] = "It Works";
std::cout << m[2][2] << std::endl;
Matrix m2(m);
std::cout << m2[2][2] << std::endl;
Matrix m3;
m3 = m2;
std::cout << m3[2][2] << std::endl;
return 0;
}
Online Demo
However, rather than using new[] manually, consider using std::vector instead (which you are already aware of, since you have #include <vector> in your code). This way, the Rule of 3/5/0 can be handled entirely by the compiler for you. std::vector and std::string are both fully compliant with the Rule, and so any compiler-generated destructor, copy constructor, and copy-assignment operator in Matrix will suffice, eg:
#include <iostream>
#include <vector>
#include <string>
class Matrix {
std::vector<std::vector<std::string>> m;
public:
Matrix(int x = 0, int y = 0) {
m.resize(x);
for (int i = 0; i < x; ++i)
m[i].resize(y);
}
class Proxy {
std::vector<std::string> &mm;
public:
Proxy(std::vector<std::string> &s) : mm(s) {}
std::string& operator[](int index) {
return mm[index];
}
};
Proxy operator[](int index) {
return Proxy(m[index]);
}
};
Online Demo
I'm just getting started with the use of templates and class templates in university, and honestly it's getting a little confusing for me.
I have to create a Vector class (custom class not std::vector) that can take an array of objects and handle operations on that array, and also automatically increase the size of the array by *2 which you can see in my addData method in Vector. My Registration class creates an array of Vector (class) by taking inputs from a file, passing them into Results so that they read in the data from the file as required.
My Vector class is meant to set the data from Registration into results and increase the size of the array if needed. How would I have to make the input and output operators in my Vector class in order to do this, similarly to how I've done it for my Registration class as it uses file input?
Vector.h:
#ifndef VECTOR_H
#define VECTOR_H
#include <iostream>
#include <string>
#include <sstream>
template <class T>
class Vector
{
public:
Vector(int arraysize1): arraysize(arraysize1), data(new T[arraysize1]){};
Vector<T> *addData(T dataToAdd){
Vector<T> *temp = new Vector<T> (this->arraysize);
temp->data = this->data;
Vector<T> *newData = new Vector<T> (this->arraysize*2);
for (int x = 0; this->arraysize; x++){
if(x < this->arraysize){
newData->setData(temp->getData()[x],x);
}
else
newData->setData(dataToAdd, x);
}
return newData;
};
void initArray(){
for(int x = 0; x<this->arraysize; x++){
this->setData(x ,x);
}
};
void printArray(){
std::ostringstream oss;
for (int i = 0; i < this->arraysize; ++i){
oss << this->data[i] + " ";
}
};
T* getData(){
return this->data;
};
int getSize(){
return this->arraysize;
}
void setData(T data, int index){
this->getData()[index] = data;
}
private:
int arraysize;
T* data;
};
#endif // VECTOR_H
Registration.h(class that makes array of 'results' which is another class)
This is how my array of objects was declared before in my header file, and now I must use my vector class so I change the declaration:
Vector<Result> results;
Registration.cpp:
Here I use input stream and osteam operator overloading to read from the file. Now that Im using my vector class for Results, what syntax do I need to change?
void Registration::readFile(istream &input){
long studentid1;
unsigned semester1;
input >> studentid1 >> semester1 >> count;
input.ignore(100, '\n');
SetStudentID(studentid1);
SetSemester(semester1);
for(unsigned i = 0; i < count; i++)
input >> results[i];
}
void Registration::writeFile(ostream & os) const{
os << "Student ID: " << GetStudentID() << '\n'
<< "Semester: " << GetSemester() << '\n';
for(unsigned i = 0; i < count; i++)
os << results[i] << '\n';
}
If my Result class is needed and any other details do let me know.
I've read many posts with the same error, unfortunately all of those deal with indexing off the end of an array. In my case I get the error when I assign the array to a variable in my constructor.
Here is my code:
Heap.cpp
#include "./Heap.h"
#include <iostream>
#include <sstream>
// Provides floor, ceil, etc.
#include <cmath>
using namespace std;
Heap::Heap() {
arraySize = 0;
n = 0;
A = NULL;
}
// This assumes that every element of the array is an
// element of the heap.
Heap::Heap(int* inArray, int inArraySize, int inHeapSize) {
// TODO: initialize your class data members. An array dynamically allocated
// as follows:
// A = new int[size];
// If you allocate an array like this you MUST deallocate it in your
// destructor. This is done for you in the destructor below.
arraySize = inArraySize;
n = inHeapSize;
A = new int[arraySize];
A = inArray;
}
// Destructor. Cleans up memory.
Heap::~Heap() {
delete [] A;
}
// Note: the function name is prefixed by Heap:: (the class
// name followed by two colons). Any function defined in
// the .cpp file must have this prefix.
int Heap::at(int i) const {
return A[i];
}
int Heap::parent(int i) const{
return (int) (i - 1) / 2;
}
int Heap::left(int i) const {
return (i + 1)* 2 - 1;
}
int Heap::right(int i) const {
return (i + 1) * 2;
}
bool Heap::hasLeft(int i) const {
int leftIndex = left(i);
std::cout << "left index = " << leftIndex<< std::endl;
return false;
}
bool Heap::hasRight(int i) const{
return false;
}
void Heap::maxHeapify(int i){
}
//
void Heap::buildMaxHeap(){
}
bool Heap::operator==(const Heap& rhs) {
if (n != rhs.n) return false;
for (int i = 0; i < n; ++i) {
if (A[i] != rhs.A[i]) return false;
}
return true;
}
bool Heap::operator==(const int* rhs) {
for (int i = 0; i < n; ++i) {
if (A[i] != rhs[i]) return false;
}
return true;
}
std::ostream& operator<<(std::ostream& out, const Heap& h) {
out << "[";
for (int i = 0; i < h.n; ++i) {
out << h.A[i];
if (i < h.n-1) {
out << ", ";
}
}
out << "]";
return out;
}
string toDotImpl(const Heap& h, int i) {
using namespace std;
stringstream ss;
if (h.hasLeft(i)) {
ss << toDotImpl(h, h.left(i));
ss << "\"" << h.at(i) << "\" -> \""
<< h.at(h.left(i)) << "\"\n";
}
if (h.hasRight(i)) {
ss << toDotImpl(h, h.right(i));
ss << "\"" << h.at(i) << "\" -> \""
<< h.at(h.right(i)) << "\"\n";
}
return ss.str();
}
string toDot(const Heap& h) {
using namespace std;
stringstream ss;
ss << "digraph G {\n";
ss << "graph [ordering=\"out\"]\n";
ss << "\"" << h.at(0) << "\"\n";
ss << toDotImpl(h, 0);
ss << "}\n";
return ss.str();
}
and
Heap.h
#pragma once
// Provides I/O
#include <iostream>
// Provides size_t
#include <cstdlib>
// Provides INT_MAX and INT_MIN
// You can consider INT_MIN to be negative infinity
// and INT_MAX to be infinity
#include <climits>
//------------------------------------------------------------
// Heap class
//------------------------------------------------------------
class Heap {
public:
// Constructor
Heap();
// This constructor assumes that every element of the array is an
// element of the heap.
Heap(int* inArray, int inArraySize, int inHeapSize);
// Destructor
~Heap();
// Accesses an element of the array.
int at(int i) const;
// Gets parent index of element at i
int parent(int i) const;
// Return element to the left of i
int left(int i) const;
// Return element to the right of i
int right(int i) const;
// Checks if an element has a left child
bool hasLeft(int i) const;
// Checks if an elemnt has a right child
bool hasRight(int i) const;
// "Max heapifies" an array
void maxHeapify(int i);
// builds a max heap
void buildMaxHeap();
// Allows comparison between results
bool operator==(const Heap& rhs);
bool operator==(const int* rhs);
// Useful for debugging. To use:
// Heap h;
// cout << h << endl;
friend std::ostream& operator<<(std::ostream& out, const Heap& h);
private:
// The array
int* A;
// Size of the array
int arraySize;
// The number of elements in the heap
int n;
};
// Useful for debugging. To use:
// Heap h;
// cout << h << endl;
std::string toDot(const Heap& h);
The code is called with I can include the entire main.cpp if needed but it has several hundred lines of just test cases that are commented out.
int A[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
Heap h(A, 8, 8);
if I comment out A = inArray; the program runs so I'm pretty confident that is where the issue is.
A is defined in Heap.h as `int* A;
Here is the full error:
*** Error in `./project': free(): invalid size: 0x00007ffd84786660 ***
Aborted (core dumped)
this is probably quite a simple issue, but I can't figure out what is causing this since I believe this should allocate an array of size inArraySize of type int and then assign the given array inArray to A.
Full disclosure: this is part of an assignment so feel free to just point me in the right direction, but my professor is fine with us using stackoverflow as long as we site it.
You're trying to copy an array, but assigning pointers like that is not how to do it. There are various ways.
Standard C++:
#include <algorithm>
std::copy(inArray, inArray + inArraySize, A);
Using standard containers:
#include <vector>
std::vector<int> A(inArray, inArray + inArraySize);
Old style C way
memcpy(A, inArray, sizeof(int) * inArraySize);
Doing:
A = new int[arraySize];
A = inArray;
Is like doing:
i = 5;
i = 6;
The second assignment overrides the first one.
Hence as a result, the member variable A is pointing to the same memory block pointed by the input argument inArray.
If you haven't dynamically allocated this memory block (with new), then you cannot dynamically deallocate it (with delete).
The lines
A = new int[arraySize];
A = inArray;
are cause of two problems.
There is a memory leak. The value returned by new int[arraySize] is lost and cannot be deallocated.
If you are calling delete [] A in the destructor, that would be cause of the second problem.
If inArray was dynamically allocated and deallocated in the calling function, you will be calling delete on the same pointer twice.
If inArray was an array created in the stack, calling delete on it is also a problem. delete can be called only on memory that was returned by call to new.
A = inArray; is not doing what you think it is doing. This line does not copy inArray into the memory you allocated for A. Instead it changes A to point to a new location (the address of inArray), causing the previously allocated memory to leak. Later on when you call delete on A you'll be trying to free memory at inArray's address.
If you just want to copy an array, you could do something like
A = new int[inArraySize];
for (i = 0; i < inArraySize; ++i)
A[i] = inArray[i];
Or better yet, with std::copy:
std::copy(inArray, inArray + inArraySize, A);
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 am little lost with templates, and how compiler processes them.
Needed some generic wrapper for std::vector<< SomeType * >>* lpVars; that is capable of performing delete on all items contained inside that vector when I delete lpVars. Something similar to the C#'s List<> (generic list class).
So I went for templates and wrote something like this:
template<class T>
class ListPtr
{
public:
std::vector<T*> items;
ListPtr()
{ }
~ListPtr()
{
size_t count = items.size();
for(size_t i=0; i<count; i++)
{
T* item = items[i];
delete item;
}
items.clear();
}
inline const int count() const
{
return (int)items.size();
}
inline T* operator[](size_t index) const
{
return items[index];
}
inline void Add(T* item)
{
items.push_back(item);
}
};
later on I declared global type lists like:
class SomeType1List : public ListPtr<SomeType1> {};
class SomeType2List : public ListPtr<SomeType2> {};
...
class SomeTypeNList : public ListPtr<SomeTypeN> {};
Here is the first part of my interest on subject:
(1) Are this classes SomeType1List, SomeType1List, ..., SomeTypeNList fully preprocessed to templates code with replaced T template type for each declaration (SomeType1, SomeType2, ..., SomeTypeN) like there is no template (are fully declared classes), or compiler performs some other magic here?
(2) And if compiler performs some other magic, how should I define this types that compiler would compile them as they are fully declared classes?
To explain usage of above code more precise:
this list instances are initialized, and returned from functions like following one:
SomeType1List* GetItems()
{
SomeType1List* items = new SomeType1List();
for(int i=0; i<1000; i++)
{
SomeType1* item = new SomeType1();
// feed item with some data
items->Add(item);
}
return items;
}
and used in other parts of code like this:
SomeType1List* items = GetItems();
int count = items->count();
for(int i=0; i<count; i++)
{
SomeType1* item = items[i];
// do something with item;
}
delete items; // all memory occupied by items released after this line
Second part of my interest on subject:
(3) Could this be written differently using only standard classes (no boost or similar sdks)? goal is speed but to keep code clean and easy to read. So this question would be is there any better way to do all of this?
Yes
No magic here
You can use std::vector<std::unique_ptr<T>> (C++11) or std::vector<std::auto_ptr<T>> (kind of deprecated)
Example:
#include <iostream>
#include <memory>
#include <vector>
class T
{
private:
unsigned int _i;
public:
void print() const
{
std::cout << "Print: " << _i << std::endl;
}
T(unsigned int i)
{
_i = i;
std::cout << "Constructor! " << _i << std::endl;
}
~T()
{
std::cout << "Destructor! " << _i << std::endl;
}
};
std::vector<std::unique_ptr<T>>* createStuff()
{
auto output = new std::vector<std::unique_ptr<T>>;
for(unsigned int i = 0; i < 5; i++)
{
output->emplace_back(new T(i));
}
return output;
}
int main()
{
std::cout << "Begin!" << std::endl;
auto stuff = createStuff();
for(std::unique_ptr<T> const& thing : *stuff)
{
thing->print();
}
delete stuff;
std::cout << "End!" << std::endl;
return 0;
}
Output:
Begin!
Constructor! 0
Constructor! 1
Constructor! 2
Constructor! 3
Constructor! 4
Print: 0
Print: 1
Print: 2
Print: 3
Print: 4
Destructor! 0
Destructor! 1
Destructor! 2
Destructor! 3
Destructor! 4
End!