Combining declaration and initialization with overloaded `=` - c++

I'm new to C++ (but not new to programming--I know Java pretty thoroughly), so this may have a simple answer I'm just overlooking.
Question in a nutshell: What (if anything) is the difference between writing:
classType a = b;
and writing
classType a;
a = b;
specifically when b is a reference variable in a function and when = is overloaded?
Details/background:
I'm working on a custom vect class, which will (eventually) hold n-tuples that behave like vectors in R^n (that is, math-style vectors, not STL-style vectors). As such, I've overloaded the = operator to copy the components from rhs to lhs.
However, I've found that, when my rhs is a reference variable in a function, the combined declaration/initialization makes the rhs and lhs reference the same object, while the separate initialization keeps them as different objects.
In my program below, this is evidenced as v2 = v1 * 10 results in both v2 and v1 being set equal to v1*10 when I use the single-line vect a = b, but behaves as expected when I write vect a; a=b in the definition of *.
Minimal Compilable Example:
I apologize for its length, but this is pared down as much as I could get it at first try. I'll keep looking for ways to reduce.
The problem occurs in the overloading of the * operator. I've commented there as to what change is required to exhibit the problem.
#include<iostream>
#include<string.h> //For toString method
#include<sstream> //For toString method
//===================================================
// Namespace for organization
namespace linalg {
class vect {
private:
//int length;
double* baseArray;
public:
//Constructor
vect() { baseArray = new double[3]; }
//TODO: Change so edits to arrayArg do not show up in the baseArray.
vect(double arrayArg[3]) { baseArray = arrayArg; }
//Returns a string for printing
std::string toString();
//Returns the element at given index
double getElementAt(int) const;
//Operator overloading:
vect& operator=(const vect &rhs);
friend vect operator*(double num, const vect &v);
};
//===================================================
// General Methods
// elementAt : return s the given element.
double vect::getElementAt(int i) const {
return baseArray[i];
}
// toString : behaves like Java convention:
//toString : gives string representation
std::string vect::toString() {
std::string retVal;
retVal.append("[");
for (int i = 0; i < 3; i++) {
//Convert the number to a string
std::ostringstream num;
num << baseArray[i];
//Append the number to the return value
retVal.append(num.str());
//Comma separated
if (i < 3-1) retVal.append(", ");
}
retVal.append("]");
return retVal;
}
//===================================================
// Operator overloads
//Overload '=' : copies the other vector into this vector
vect& vect::operator= (const vect &rhs) {
//Check for self-assignment:
if (this == &rhs) {
return *this;
}
//Copy the rhs into this vector
for (int i = 0; i < 3; i++) {
baseArray[i] = rhs.getElementAt(i);
}
return *this;
}
//Overload scalar multiplication
vect linalg::operator*(double num, const vect &v) {
//CHANGE THIS to a single-line assignment/initialization, and the problem occurs.
vect result;
result = v;
for(int i = 0; i < 3; i++) {
result.baseArray[i] *= num;
}
return result;
}
};
using namespace linalg;
using namespace std;
int main() {
double a[3] = {2, 4, 8};
double b[3] = {5, 1, 9};
vect v1(a);
vect v2(b);
v1 = 10*v2;
cout << v1.toString() << endl;
cout << v2.toString() << endl;
system("pause"); //Pause the windows terminal
return 0;
}

The difference is that vect a = b; performs copy initialization whereas vect a; a = b; performs copy assignment. Copy initialization calls the copy constructor, whereas copy assignment calls the overloaded operator=.
In your code, your copy assignment operator copies one vector's elements over into another, which is good. However, your copy constructor simply sets baseArray of the LHS to point to the same block of memory as that of the RHS, without copying any vector elements. When two pointers point to the same memory, of course modifying one also modifies the other.

Related

Sorting the Addressess of the pointers in a dynamically allocated array of class

I've created a dynamically allocated array of a class.
class AlienShip
{
public:
int DAMAGE;
..........
};
AlienShip * ships = new AlienShip[N];
Where N is between 2 and 50 inclusively. After initialization of each element, I want to sort the array in the decreasing value of DAMAGE in each of ships.
I implemented a simple bubble sort to do the same. However, instead of moving whole objects around, I want to move the addresses of pointers. I did something like this:
....
if(ships[j].DAMAGE < ships[j+1].DAMAGE)
swap( ships + j, ships + j + 1)
....
But it shows a compile-time error, saying it didn't find any matching function swap. I think the swap() is defined as a template in <cstdlib> then why does it not work?
EDIT: I discovered that sorting the pointer address does NOT necessarily sorts the array. 😅 But I wrote a swap function to sort some pointer address but it didn't have any effect.
mySwap(int* a, int* b){
int *tmp;
tmp = a, a = b, b = tmp;
}
I wanted to swap the address of a and b. So that *b refers to previous *a. But it didn't work. I wonder why?
Since you have an array of AlienShips, and you want to sort on the DAMAGE variable of each, then use std::sort with a custom comparison function. You don't need to sort pointers. You have one pointer (to the start of your array). Using that pointers, you get each AlienShip.
If you want to swap a pair of AlienShips, you can take advantage of the fact that you should have a default assignment operator for your class.
swap(AlienShip& a, AlienShip& b) {
AlienShip temp = a;
a = b;
b = temp;
}
But instead of having a pointer to dynamically acquired memory using new, you should either statically allocate memory (using std::array) or dynamically allocate memory (using std::vector). Then you can use std::sort in <algorithm> to easily sort your array.
Example follows:
#include <algorithm> // for std::sort
#include <functional> // for std::greater
#include <array> // for std::array
#include <iostream> // for std::cout and std::ostream
#include <vector> // for std::vector
constexpr int num_alien_ships = 10;
class AlienShip
{
public:
AlienShip(int damage)
: damage_(damage)
{}
inline const int& damage() const {
return damage_;
}
private:
int damage_;
};
// This operator<< overload is provided for convenience in showing that
// the ships are actually sorted below
std::ostream& operator<<(std::ostream& os, const AlienShip& alien_ship) {
os << alien_ship.damage();
return os;
}
// If you define a greater-than operator for your AlienShips,
// then you can use the std::greater template from <algorithm>
// to sort from big damage to small damage
bool operator>(const AlienShip& a, const AlienShip& b) {
return a.damage() > b.damage();
}
int main() {
std::array<AlienShip, num_alien_ships> alien_array = { 1, 9, 2, 8, 3, 7, 4, 6, 5, 0 };
std::sort(alien_array.begin(), alien_array.end(),
[](const AlienShip& a, const AlienShip& b) { return a.damage() > b.damage(); }
);
std::cout << "Result of sorting alien_array using a custom lambda:\n";
for (const AlienShip& a : alien_array) {
std::cout << a << ' ';
}
std::cout << '\n';
std::cout << "Result of sorting alien_vector using std::greater:\n";
std::vector<AlienShip> alien_vector = { 8, 6, 7, 5, 3, 0, 9 };
std::sort(alien_vector.begin(), alien_vector.end(), std::greater<AlienShip>());
for (const AlienShip& a : alien_vector) {
std::cout << a << ' ';
}
std::cout << '\n';
}
Use the overloaded operator> function to define ordering for your AlienShips if it makes sense for them to be sorted by damage_. If that order only makes sense in the context of this single sort operation, stick to the lambda.
If you really want to sort by moving around pointers, them you need an actual array of pointers:
AlienShip* alien_ship_ptr[10];
std::for_each(std::begin(alien_ship_ptr), std::end(alien_ship_ptr), [](AlienShip*& asp){ asp = new AlienShip; });
After populating your array of pointers, then you can sort them using the methods described above.
You can only swap variables. ships + j is not a variable reference.
ships have AlienShip type and j probably is int in your case,
try this swap(ships[j],ships[j+1])

operator method and returning "out-of-scope" object?

#include <iostream>
using namespace std;
class Box {
public:
double getVolume(void) {
return length * breadth * height;
}
void setLength( double len ) {
length = len;
}
void setBreadth( double bre ) {
breadth = bre;
}
void setHeight( double hei ) {
height = hei;
}
// Overload + operator to add two Box objects.
Box operator+(const Box& b) {
Box box; //local object?
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
The source of the code: https://www.tutorialspoint.com/cplusplus/cpp_overloading.htm. How does the above operator+ work? What I'm confused is that as opposed to Java, in C++ Box box creates an object on the stack, but the method is returning the object whose lifetime is limited to that scope of method (operator).
So I tried another example:
template <typename T>
class SmartPointer
{
T *ptr;
int numElem; //-1 if an element. >=0 if an array
public:
SmartPointer(T pNum);
SmartPointer();
SmartPointer(T *pArray, int pSize);
~SmartPointer();
SmartPointer(std::initializer_list<T> nums);
T getValue() const;
T getValue(int index) const;
void setValue(T pNum);
void setValue(T pNum, int index);
int getNumElem() const;
SmartPointer<T> operator+ (const SmartPointer<T>& ptr);
SmartPointer<T> operator- (const SmartPointer<T>& ptr);
SmartPointer<T> operator* (const SmartPointer<T>& ptr);
};
template <class T>
SmartPointer<T> SmartPointer<T>::operator+ (const SmartPointer<T>& p_ptr)
{
int pSize = this->getNumElem();
T tempArray[pSize] = {0};
for(int i = 0; i < this->getNumElem(); i++)
{
int result = this->getValue(i) + p_ptr.getValue(i);
tempArray[i] = result;
}
SmartPointer<T> result(tempArray, pSize); (line 60)
return result; (line 61)
}
}
I am trying to implement smartpointer, and I want to overload + as if it were a componentwise addition (like vector addition).
Then, if I run the following code:
SmartPointer<int> sPointer6({10,11,12});
SmartPointer<int> sPointer7({10,11,12});
SmartPointer<int> sPointer8 = sPointer6 + sPointer7;
cout << sPointer8.getValue(0) << endl; //getValue(index)
cout << sPointer8.getValue(1) << endl;
cout << sPointer8.getValue(2) << endl;
I get the following output:
1310912
1338712
24
But if I replace line 60 and line 61 by
return SmartPointer<T>(tempArray, pSize);
Then I get the following output:
20
22
24
Why am I getting different outputs? And why does the first example work but not the smartpointer example?
What I'm confused is that as opposed to Java, in C++ Box box creates an object on the stack, but the method is returning the object whose lifetime is limited to that scope of method (operator).
Box operator+(const Box& b) {
Box box; //local object?
// ...
return box;
}
Correct but this isn't a problem because the object is copied.
The Box object define a implicit default "copy constructor" (with signature Box (Box const & b)) and an implicit operator=() (with signature Box & (Box const & b). Starting from C++11, also a "move constructor" (with signature Box (Box && b)) and a Box & operator=(Box const & b)
So, when you write
Box a, b, c;
// ...
Box d { a + b };
c = a + b;
the operator+() create a temporary Box (result) that is copied (or moved) in d, through copy or move constructor, or in c, through operator=(), before it's destruction.
For Box, default copy/move constructors and operator=()'s are OK because there isn't involved memory allocation or other complex operations (they simply copy length, breadth, height).
The problems arises with your SmartPointer that (if I understand correctly) dynamically allocated memory (for ptr).
A default constructor (etc.) ins't OK anymore because it copy the value of ptr that is (destructing the temporary object) immediately deallocated (if you have added delete[] in ~SmartPointer().
The result is that when you write
SmartPointer<int> sPointer8 = sPointer6 + sPointer7;
the ptr in sPointer8 points to a memory area that is free. And that the program can use for other purposes (other variables).
So, when you get
1310912
1338712
24
it's because (I suppose) the memory that was reserved for result[0] and result[1] in the temporary sPointer6 + sPointer7 is free and reused for one or more other variables.
The fact that you get different results from
SmartPointer<T> result(tempArray, pSize);
return result;
and from
return SmartPointer<T>(tempArray, pSize);
it's a pure fatality, because in both cases you're accessing free memory that is, in both case, UB (Undefined Bahaviour).
And UB mean: anything can happen.
Solution: write copy/move constructors, and both operator=() to manage the duplication and copy of memory allocated in ptr.
Or, better, avoid the direct management of memory and use containers/smart pointers available in standard libraries.
Another point:
int pSize = this->getNumElem();
T tempArray[pSize] = {0};
isn't (standard C++): you can't initialize a C-style array with a run-time value.
Your template class SmartPointer does not define operator= and copying constructor, thus a default operator= or copying constructor is defined. The returned copy sPointer8references freed array ptr. It is UB, it comes due to violated The Rule of Free.
You would not meet such error if you would use std::vector<T> instead of C-array ptr and its size numElem.

Arithmetic and Assignment operator overloading - return values, scope, combining expressions

The code I have so far:
#include <iostream>
#include <vector>
using namespace std;
class Dictionary
{
private:
string dictName;
struct wordCard
{
string word;
string translation;
};
vector<wordCard> Dict;
bool foundit = false;
public:
// My attemtp at swap function for copy-and-swap:
void swap(Dictionary& dict1, Dictionary& dict2)
{
Dictionary dict3("tmp");
dict3.dictName = dict1.dictName;
dict3.Dict = dict1.Dict;
dict1.dictName = dict2.dictName;
dict1.Dict = dict2.Dict;
dict2.dictName = dict3.dictName;
dict2.Dict = dict3.Dict;
}
// Very basic constructor (setting the dictionary name while creating an object was part of the assignment):
Dictionary(string name)
{
setDictName(name);
}
/* various functions that work fine */
// Overloading "+" operator:
// The result is supposed to be a new dictionary (without changing the source) where all words from the
// original dictionaries are present without doubles.
Dictionary& operator+ (const Dictionary& dict)
{
bool doubleword = false;
string plusname;
plusname = "Augmenting " + this->dictName + " & " + dict.dictName;
Dictionary plusDict(plusname);
plusDict.Dict = this->Dict;
for (int i = 0; i < dict.Dict.size(); i++)
{
doubleword = false;
for (int i2 = 0; i2 < plusDict.Dict.size(); i2++)
{
if (plusDict.Dict[i2].word == dict.Dict[i].word)
{
doubleword = true;
}
}
if (!doubleword)
{
plusDict.Dict.push_back(dict.Dict[i]);
}
}
return *this;
}
/* 2 other overloads that are very similar */
// Overloading "=" operator (using copy-and-swap):
// Not part of the assignment, but I couldn't think of another way to make the other operators work.
Dictionary& operator=(Dictionary dict)
{
swap(*this, dict);
return *this;
}
};
And the problems I have with it:
Ideally, it should work like this:
Obj1 = result of operation Obj2 + Obj3;
What I'm getting at the moment is:
Obj1 = Obj2 (ignores Obj3)
I have a vague idea why it happens (or, actually, two ideas). First, operator+ returns *this, not the actual result. But when I tried to change it to the temp class object, compiler started screaming at me. Second, I'm aware that I'm using a local variable (temp class object), but I don't know how to make it public so I could use it later. When I try to add a class object to the public: section (or private:), the compiler treats it as a function declaration, not a class object.
So, how can I either make my temp class object public, or return result of a+b instead of *this, or make operator= catch the result or operator+ instead of what it returns?
operator + should return a new object by value and be const - i.e. something like
Dictionary operator+ (const Dictionary& dict) const
{
Dictionary ret;
//modify ret
return ret;
}

C++ Operator Overload Error

I'm trying to create my own version of an array called a safearray, to test my knowledge of operator overloading and creating proper class's and such.
I'm encountering two errors.
SafeArray.h:11:15: error: ‘const int SafeArray::operator’ cannot be overloaded
SafeArray.h:10:10: error: with ‘int& SafeArray::operator’
My code is split between three files.
Main.cpp
#include <cstdlib>
#include <iostream>
#include "SafeArray.h"
using namespace std;
int main(int argc, char** argv) {
SafeArray a(10); // 10 integer elements
for (int i = 0; i < a.length(); i++) {
cout << i << " " << a[i] << "s" << endl; // values initialise to 0
}
cout << endl << a[1]; // Program exits here.
a[3] = 42;
cout << a[3];
a[10] = 10;
cout << a[10];
a[-1] = -1; // out-of-bounds is "safe"?
SafeArray b(20); // another array
b = a; // array assignment
for (int i = 0; i < b.length(); i++) {
cout << b[i] << endl; // values copied from a
}
return 0;
}
SafeArray.h
#ifndef SAFEARRAY_H
#define SAFEARRAY_H
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
// const SafeArray operator= (const SafeArray&);
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
SafeArray(SafeArray const &other);
~SafeArray();
private:
int length_;
int *array;
//int array[];
};
#endif /* SAFEARRAY_H */
SafeArray.cpp
#include "SafeArray.h"
#include <iostream>
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
int SafeArray::length() {
return this->length_;
}
int SafeArray::boundsCheck(int y) {
}
int& SafeArray::operator[] (int y) {
return array[y];
}
SafeArray::~SafeArray() {
delete [] array;
}
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Your class definition isn't valid. int array[] is an incomplete type, which must not appear as a (non-static) class member. Some compilers accept this as a synonym for int array[0], but zero-sized arrays are not valid in C++, either (only in C99).
In short, you cannot write your code the way you do. You need to learn about dynamic allocation and manage your own memory. Check out how std::vector is implemented.
In C++11, I might recommend a std::unique_ptr<int[]> array as a quick-fix approach, to be initialized as array(new int[x]).
Actually int array[] is valid, and may appear as a class member. The following compiles with strict C++11 conformance:
class foo
{
public:
foo() {}
int length;
int A[];
};
void ralph()
{
foo *bar = (foo *)new int[ 21 ];
bar->length = 20;
bar->A[0] = 1;
}
This is legal, and has its advantages (occasionally). Although it is not commonly used.
However, I suspect that the OP wanted something more along the lines of
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
int& operator[] (int y);
const int operator [] (const int y) // you need this one too.
private:
int length_;
int *array;
};
along with
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
As #Kerrek already pointed out, your class definition is clearly wrong (shouldn't compile).
To fix it, you want to change the definition to something like:
int *array;
Then in your default ctor you could use something like this:
SafeArray::SafeArray(unsigned size = 0)
: array(new int[size])
{
for (unsigned i=0; i<size; i++)
array[i] = 0;
}
Then, yes, you'll need to write an assignment operator. The usual way is called the copy and swap idiom. You create a copy, then swap the contents of the current one with those of the copy:
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
Along with that, you'll need a copy constructor that makes a copy of the data as well:
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Finally, you'll need a destructor to destroy an object and (particularly) delete the memory it holds:
SafeArray::~SafeArray() {
delete [] array;
}
Then realize that all of that is an ugly mess that will never really work well. In particular, the basic methodology is restricted to an array that's basically fixed in size. As long as you only store ints, it's fairly easy to overlook the problems, and make a dynamic array that (sort of) works. When/if you want to store some other type, however, you just about need to separate allocating memory from initializing objects in that memory, which means throwing away essentially all the code above, and replacing it with something that:
keeps track of the array size and allocation size separately
allocates memory with ::operator new, an Allocator object, or something else similar
uses placement new to initialize objects in the memory when needed.
uses explicit destructor calls to destroy the objects
uses ::operator delete to release memory
and so on. To summarize, std::vector is not a trivial piece of work.
The error message refers to these two lines:
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
Your error message says that (int y) and (const int y) are too similar to be two different overloads of the [] operator. You cannot overload on (int y) and (const int y) because the calls would all be ambiguous.
You probably meant to return a const int if your SafeArray is const, but return an int& if your SafeArray is not const. In that case, you declare the second function to apply to const SafeArray, by putting the word const after the parameter list. This is what you should write in SafeArray.h:
int& operator[] (int y);
const int operator [] (int y) const; // you need this one too.
You would then have to write both of these functions in SafeArray.cpp:
int& SafeArray::operator[] (int y) {
return array[y];
}
const int SafeArray::operator[] (int y) const { // you need this one too.
return array[y];
}

C++ Operator overloading - 'recreating the Vector'

I am currently in a collage second level programing course... We are working on operator overloading... to do this we are to rebuild the vector class...
I was building the class and found that most of it is based on the [] operator. When I was trying to implement the + operator I run into a weird error that my professor has not seen before (apparently since the class switched IDE's from MinGW to VS express...) (I am using Visual Studio Express 2008 C++ edition...)
Vector.h
#include <string>
#include <iostream>
using namespace std;
#ifndef _VECTOR_H
#define _VECTOR_H
const int DEFAULT_VECTOR_SIZE = 5;
class Vector
{
private:
int * data;
int size;
int comp;
public:
inline Vector (int Comp = 5,int Size = 0)
: comp(Comp), size(Size) { if (comp > 0) { data = new int [comp]; }
else { data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE; }
}
int size_ () const { return size; }
int comp_ () const { return comp; }
bool push_back (int);
bool push_front (int);
void expand ();
void expand (int);
void clear ();
const string at (int);
int& operator[ ](int);
int& operator[ ](int) const;
Vector& operator+ (Vector&);
Vector& operator- (const Vector&);
bool operator== (const Vector&);
bool operator!= (const Vector&);
~Vector() { delete [] data; }
};
ostream& operator<< (ostream&, const Vector&);
#endif
Vector.cpp
#include <iostream>
#include <string>
#include "Vector.h"
using namespace std;
const string Vector::at(int i) {
this[i];
}
void Vector::expand() {
expand(size);
}
void Vector::expand(int n ) {
int * newdata = new int [comp * 2];
if (*data != NULL) {
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
newdata -= comp;
comp += n;
data = newdata;
delete newdata;
}
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
bool Vector::push_back(int n) {
if (comp = 0) { expand(); }
for (int k = 0; k != 2; k++) {
if ( size != comp ){
data[size] = n;
size++;
return true;
}
else {
expand();
}
}
return false;
}
void Vector::clear() {
delete [] data;
comp = 0;
size = 0;
}
int& Vector::operator[] (int place) { return (data[place]); }
int& Vector::operator[] (int place) const { return (data[place]); }
Vector& Vector::operator+ (Vector& n) {
int temp_int = 0;
if (size > n.size_() || size == n.size_()) { temp_int = size; }
else if (size < n.size_()) { temp_int = n.size_(); }
Vector newone(temp_int);
int temp_2_int = 0;
for ( int j = 0; j <= temp_int &&
j <= n.size_() &&
j <= size;
j++) {
temp_2_int = n[j] + data[j];
newone[j] = temp_2_int;
}
////////////////////////////////////////////////////////////
return newone;
////////////////////////////////////////////////////////////
}
ostream& operator<< (ostream& out, const Vector& n) {
for (int i = 0; i <= n.size_(); i++) {
////////////////////////////////////////////////////////////
out << n[i] << " ";
////////////////////////////////////////////////////////////
}
return out;
}
Errors:
out << n[i] << " "; error C2678:
binary '[' : no operator found which
takes a left-hand operand of type
'const Vector' (or there is no
acceptable conversion)
return newone;
error C2106: '=' : left
operand must be l-value
As stated above, I am a student going into Computer Science as my selected major I would appreciate tips, pointers, and better ways to do stuff :D
This:
int operator[ ](int);
is a non-const member function. It means that it cannot be called on a const Vector.
Usually, the subscript operator is implemented such that it returns a reference (if you return a value, like you are doing, you can't use it as an lvalue, e.g. you can't do newone[j] = temp_2_int; like you have in your code):
int& operator[](int);
In order to be able to call it on a const object, you should also provide a const version of the member function:
const int& operator[](int) const;
Since you ask for "tips, pointers, and better ways to do stuff:"
You cannot name your include guard _VECTOR_H. Names beginning with an underscore followed by a capital letter are reserved for the implementation. There are a lot of rules about underscores.
You should never use using namespace std in a header.
Your operator+ should take a const Vector& since it is not going to modify its argument.
Your at should return an int and should match the semantics of the C++ standard library containers (i.e., it should throw an exception if i is out of bounds. You need to use (*this)[i] to call your overloaded operator[].
You need to learn what the * operator does. In several places you've confused pointers and the objects to which they point.
Watch out for confusing = with == (e.g. in if (comp = 0)). The compiler will warn you about this. Don't ignore warnings.
Your logic will be much simpler if you guarantee that data is never NULL.
Can't fit this into a comment on Neil's answer, so I'm gonna have to go into more detail here.
Regarding your expand() function. It looks like this function's job is to expand the internal storage, which has comp elements, by n elements, while maintaining the size of the Vector. So let's walk through what you have.
void Vector::expand(int n) {
int * newdata = new int [comp * 2];
Okay, you just created a new array that is twice as big as the old one. Error: Why doesn't the new size have anything to do with n?
if (*data != NULL) {
Error: *data is the first int element in your array. It's not a pointer. Why is it being compared to NULL?
Concept Error: Even if you said if (data != NULL), which could be a test to see if there is an array at all, at what point in time is data ever set to NULL? new [] doesn't return NULL if it's out of memory; it throws an exception.
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
Warning: You're copying the whole array, but only the first size elements are valid. The loop could just run up to size and you'd be fine.
newdata -= comp;
Error: Bad pointer math. newdata is set to a pointer to who knows where (comp ints back from the start of newdata?!), and almost certainly a pointer that will corrupt memory if given to delete [].
comp += n;
This is fine, for what it is.
data = newdata;
delete newdata;
}
Error: You stored a pointer and then immediately deleted its memory, making it an invalid pointer.
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
Error: This should be in your constructor, not here. Again, nothing ever sets data to NULL, and *data is an int, not a pointer.
What this function should do:
create a new array of comp + n elements
copy size elements from the old array to the new one
delete the old array
set data to point to the new array
Good luck.
Besides of what others already wrote about your operator[]():
Your operator+() takes the right-hand side per non-const reference - as if it would attempt to change it. However, with A+B everyone would expect B to remain unchanged.
Further, I would implement all binary operators treating their operands equally (i.e., not changing either of them) as non-member functions. As member functions the left-hand side (this) could be treated differently. (For example, it could be subjected to overwritten versions in derived classes.)
Then, IME it's always good to base operator+() on operator+=(). operator+=() does not treat its operands equally (it changes its left one), so it's best done as a member function. Once this is done, implementing operator+() on top of it is a piece of cake.
Finally, operator+() should never, never ever return a reference to an object. When you say A+B you expect this to return a new object, not to change some existing object and return a reference to that.
There are so many errors in your code that it is hard to know where to start. Here's one:
delete [] data;
*data = *newdata;
You delete a pointer and then immediately dereference it.
And:
const string Vector::at(int i) {
this[i];
}
This is (I think) a vector of ints. why is this returning a string? And applying the [] operator to this does not call your operator[] overload - it treats this as an array, which it isn't.
You need to provide two versions of your operator[]. For accessing:
T operator[](std::size_t idx)const;
For writing to the element:
T& operator[](std::size_t idx);
In both of the above, replace T with the type of the elements. The reason you have this problem is that only functions that are marked "const" may be invoked on an object declared to be "const". Marking all non-mutating functions as "const" is definitely something you should do and is called "const-correctness". Since returning a reference to an element (necessary for writing) allows the underlying object to be mutated, that version of the function cannot be made "const". Therefore, a read-only "const" overload is needed.
You may also be interested in reading:
Const Correctness from the C++ FAQ Lite
Const Correctness in C++
int Vector::operator[] (int place) { return (data[place]); }
This should be
int Vector::operator[] (int place) const { return (data[place]); }
so that you will be able to do the [] operation on const vectors. The const after the function declaration means that the class instance (this) is treated as const Vector, meaning you won't be able to modify normal attributes. Or in other words: A method that only has read access to attributes.