Problem with dynamic array in constructor, cannot convert char* to char - c++

I can't use string because of task conditions, so it must be char*. There is problem with dynamic array in constructor. Error "'=': cannot convert 'char*' to 'char'"
#include <iostream>
class Line {
public:
const int Size = 10;
char* DynamicLines = new char[Size];
int counter = 0;
Line(char* otherline) {
if (counter < Size) {
DynamicLines[counter] = otherline;
counter++;
}
std::cout << "constructor called\n";
}
~Line() {
delete[] DynamicLines;
std::cout << "destructor called\n";
}
void Print();
};
void Line::Print() {
this->counter = 0;
std::cout << DynamicLines[counter] << "\n" << "length = ";
counter++;
}
void main()
{
char* temp = new char;
std::cin >> temp;
Line Fline(temp);
Fline.Print();
delete temp;
system("pause");
}

According to the C++ Standard the function main shall have the return type int
int main()
In this constructor
Line(char* otherline) {
if (counter < Size) {
DynamicLines[counter] = otherline;
counter++;
}
std::cout << "constructor called\n";
}
this statement
DynamicLines[counter] = otherline;
does not make a sense. In the left hand side of the assignment there is an object of the type char while in the right hand side there is an object of the type char *. So the compiler issues the error message.
You need to copy elements of one array into another array.
Also the condition of this if statement
if (counter < Size) {
always evaluates to true. So the statement also does not make a sense.
And in main this code snippet can result in undefined behavior because you allocated only one character
char* temp = new char;
std::cin >> temp;
but are trying to enter a string.

char *DynamicLines is pointer to an array of char. Therefore each entry in the array, is a char, not a char* (pointer).
Your constructor accepts a pointer to char which needs to be dereferenced, in order to access the char in the memory and store it in the array.
So:
Line(char* otherline) {
if (counter < Size) {
DynamicLines[counter] = *otherline; // Notice * in front, to get the value from the pointer
counter++;
}
std::cout << "constructor called\n";
}

Related

I have overloaded << and = operators. Why when I assign an object to another and try to print it I get junk printed out?

I have a program with a class Length. This class has an attribute named size of type int and a dynamic
array *numb of type char. I have overloaded operators << and = so that I can print object values and assign values of an object to another.
If I leave the return type of operator = to void, the program seems to work fine but if I try to return a Length object I get junk printed. Why?. Thank you.
Here is my code:
class Length
{
int size;
char *numb;
public:
Length()
{
}
Length(int size, char *n);
~Length();
friend ostream & operator << (ostream &channel, const Length &l);
Length operator = (const Length &x);
};
Length::Length(int size, char *n)
{
this->size = size;
numb = new char[size];
for(int i = 0; i < size; i++)
{
numb[i] = n[i];
}
}
ostream & operator << (ostream &channel, const Length &l)
{
channel << "Size " << l.size <<endl;
for(int i = 0; i < l.size; i++)
{
channel << l.numb[i] << endl;
}
return channel;
}
Length Length::operator =(const Length &x)
{
delete [] this->numb;
this->size = x.size;
this->numb = new char[this->size];
memcpy(this->numb, x.numb, this->size);
return *this; //If I comment that line and make return type void programm works fine
}
int main()
{
char * ch = "Hello";
char * cx = "Hi";
int size = strlen(ch);
int size_x = strlen(cx);
Length h(size, ch);
Length x(size_x, cx);
cout << x; //Works fine
cout << h <<endl; //Works fine
x = h;
cout << x <<endl; //Prints junk
}
To expand on what molbdnilo said, there are two problem with your code. the first problem is that operator= should return a reference. It's not an error when it doesn't but it results in behaviour that is inconsistent with how assignment normally behaves. I'm not going into the details here, you can look them up if you like.
But when combined with your second error you do get problems. Because you are returning Length by value from operator= you invoke the Length copy constructor. But your class doesn't have a copy constructor so it uses the default, and that default does the wrong thing. What happens is that when you return from your operator= the this->numb pointer gets copied to the temporary object that is the return value of operator=. That temporary object then gets destroyed, the consequence of that is that the memory that this->numb is pointing at gets deleted. And that is why you see garbage when you print out x, because it's internal memory has been freed.
Two fixes are possible, return a reference from operator=, and write a valid copy constructor for your class. Either would fix the problem, you should do both.
And you should also read up on the rule of three, to fully understand this very important issue.
You should return here reference:
Length& Length::operator =(const Length &x)
{
if (this != &x)
{
delete [] this->numb;
this->size = x.size;
this->numb = new char[this->size];
memcpy(this->numb, x.numb, this->size);
}
return *this;
}
And add copy constructor:
Length(Length& len)
{
size = len.size;
numb = new char[size];
for (int i = 0; i < size; ++i)
{
numb[i] = len.numb[i];
}
}

Passing pointer to calling function but called function parameter as reference

I have tried this piece of code mentioned below, I'm not understanding why *p need to be pass to doOperation() function. Why can't we pass p? What is the difference between the two?
doOperation(*p); // need explanation why derefencing
doOperation(p); // Gives compilation error
int main()
{
int *p = getpointer();
std::cout << "p:" << *p << std::endl;
doOperation(*p); // Why this has to be pass as a pointer when the function parameter as reference
return 0;
}
void doOperation(int &ptr)
{
//std::cout << "ptr:" << ptr << std::endl;
}
int *getpointer()
{
int *ptr = new int[10];
int i;
for (i=0; i <= 10; i++)
{
*(ptr+i) = i;
}
return ptr;
}
You've declared p as an integer pointer. The function doOperation takes an int reference as a parameter. doOperation(*p) means that you're dereferencing the pointer (which points to the first element in the array) and passing it to the function. Also as #dxiv have pointed out, in the function getpointer, the loop initializes 11 elements instead of 10. you can solve this by just changing <= to <.
If you want to pass the pointer by reference instead, the function doOperation can look like this:
void doOperation(int *&ptr)
{
std::cout << "ptr:" << ptr << std::endl;
}
Then you can just pass the pointer as an argument like this:
doOperation(p);
Output:
p:0
ptr:0x2b71550
The code after the changes should look like this:
#include <iostream>
void doOperation(int *&ptr)
{
std::cout << "ptr:" << ptr << std::endl;
}
int *getpointer()
{
int *ptr = new int[10];
int i;
for (i=0; i < 10; i++)
{
*(ptr+i) = i;
}
return ptr;
}
int main()
{
int *p = getpointer();
std::cout << "p:" << *p << std::endl;
doOperation(p);
return 0;
}

Is my destructor giving me this error: *** Error in `./main': double free or corruption (fasttop):?

This is the instructions for my assignment:
The Copy Constructor. The copy constructor should perform a deep copy of the argument object, i.e. it should construct an IntCollection with the same size and capacity as the argument, with its own complete copy of the argument's data array.
The Assignment Operator (=). The assignment operator should also perform a deep copy of the argument object. It must return itself (or more efficiently, a reference to itself) in order tosupport multiple assignments on the same line, e.g. a = b = c. If you implement your assignment operator first it could be used in the copy constructor, but this is not a requirement.
The Is Equals operator (==). The "is equals" operator should return true if the argument object has the same size as the receiving object, and the values in both objects’ data arrays are identical.
The insertion operator (<<). The insertion operator should add the int parameter into the receiving IntCollection. The functionality is exactly the same as the add() function, i.e. add ints to the collection. Note, however, that this function must return a reference to itself in order to support multiple insertions on the same line, e.g. c << 45 << -210. Unlike the assignment operator, this return must be done by reference, because each insertion actually modifies the IntCollection object, and insertion is done from left to right.
The destructor. Function add() calls addCapacity() to allocate memory when it needs more room. Nowhere in this program is the memory deallocated with delete [], which means we have a memory leak! Add a destructor which correctly handles this.
addCapacity. Note that addCapacity() is a private member function. What happens if you try to call it from outside the class, i.e. by adding the line below to main()?
c.addCapacity();
Here is my code:
IntCollection.h:
#ifndef INTCOLLECTION_H
#define INTCOLLECTION_H
// Allocate memory in chunks of ints of this size.
const int CHUNK_SIZE = 5;
class IntCollection
{
private:
// The number of ints currently stored in the int
int size;
// the total number of elements available for storage
// in the data array
int capacity;
// A pointer to the dynamically allocated data array
int* data;
// a private member function to allocate more memory
// if necessary
void addCapacity();
public:
// Constructor
IntCollection();
// Destructor
~IntCollection();
// Copy constructor:
IntCollection(const IntCollection &c);
void add(int value);
int get(int index);
int getSize();
IntCollection& operator=(const IntCollection &c);
bool operator==(const IntCollection &c);
IntCollection& operator<<(int value);
};
#endif
IntCollection.cpp:
#include "IntCollection.h"
#include <cstdlib>
#include <iostream>
using namespace std;
IntCollection::IntCollection()
{
// Initialize member data to reflect an empty
// IntCollection
size = capacity = 0;
data = NULL;
}
IntCollection::~IntCollection()
{
delete [] data;
}
IntCollection::IntCollection(const IntCollection &c) {
size = c.size;
capacity = c.capacity;
data = c.data;
for (int i = 0; i < c.size; i++)
{
data[i] = c.data[i];
}
}
void IntCollection::addCapacity()
{
// Create a new, bigger buffer, copy the current data to
// it, delete the old buffer, and point our data
// pointer to the new buffer
int *newData;
data = new int[capacity];
capacity += CHUNK_SIZE;
newData = new int[capacity];
for (int i = 0; i < size; i++)
{
newData[i] = data[i];
delete[] data;
data = newData;
}
}
void IntCollection::add(int value)
{
// first, allocate more memory if we need to
if (size == capacity)
{
addCapacity();
}
// Now, add the data to our array and increment size
data[size++] = value;
}
int IntCollection::get(int index)
{
if (index < 0 || index >= size)
{
cout << "ERROR: get() trying to access index out of range.\n";
exit(1);
}
return data[index];
}
int IntCollection::getSize()
{
return size;
}
IntCollection &IntCollection::operator=(const IntCollection &c)
{
size = c.size;
capacity = c.capacity;
data = c.data;
return *this;
}
bool IntCollection::operator==(const IntCollection &c)
{
if ((size == c.size) && (capacity == c.capacity))
{
for (int m = 0; m < size; m++)
{
if (data[m] == c.data[m])
{
continue;
} else
{
return false;
}
}
}
return true;
}
IntCollection &IntCollection::operator<<(int value)
{
add(value);
return *this;
}
main.cpp:
#include "IntCollection.h"
#include <iostream>
using namespace std;
int main()
{
IntCollection c;
c.add(45);
c.add(-210);
c.add(77);
c.add(2);
c.add(-21);
c.add(42);
c.add(7);
for (int i = 0; i < c.getSize(); i++)
{
cout << c.get(i) << endl;
}
IntCollection d(c);
for (int i = 0; i < c.getSize(); i++)
{
cout << c.get(i) << endl;
}
IntCollection e;
e = c;
cout << "Testing = Overload" << endl;
for(int i = 0; i < c.getSize(); i++)
{
cout << c.get(i) << endl;
}
IntCollection f;
f<<8<<9<<10;
cout << "Testing<<Overload" << endl;
for(int i = 0; i < f.getSize(); i++)
{
cout << f.get(i) << endl;
}
cout << "Testing == Overload" << endl;
c.add(10);
if(f == c)
{
cout << "Both objects match" << endl;
}
else
{
cout << "They don't match" << endl;
}
return 0;
}
After I believed that I fixed most of my errors, I get this as my output:
*** Error in `./main': double free or corruption (fasttop): 0x0000000000b2ec80 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7ff7e6f70bfb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7ff7e6f76fc6]
/lib/x86_64-linux-gnu/libc.so.6(+0x7780e)[0x7ff7e6f7780e]
./main[0x400fa1]
./main[0x400fe2]
./main[0x400aa2]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7ff7e6f202e1]
./main[0x40097a]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:01 11008976 /home/runner/main
I won't post all of it because it is long. Is my destructor causing this? I am not sure how to fix this nor have I ever encountered this error before.
This line, from your copy constructor, is the likely problem:
data = c.data;
After that line, you have two object pointing to the same data. It is a shallow copy.
If one of the objects are destructed then if will delete[] the data, leaving the other object with an invalid pointer. When the second objects destructor then tries to delete[] the data (again!) it will lead to undefined behavior.
You need to do a deep copy, which is the assignment you have. That includes a new memory allocation and actually copying the data.
Same thing with the copy-assignment operator.

Difference in allocating char type and int type in C++

I have a class foo like this:
class foo
{
private:
int* a;
public:
foo()
{
a = new int[4];
cout << "a" << endl;
}
};
When I create new object named foo1 and then I debug, after the allocating line, it yields the result: a 0x005a4580 {-842150451}.
But when I replace all int-s by char-s in class definition, it yields an undesired result:
a 0x005694a0 "ÍÍÍÍýýýý\x6ŒÒ•\x5Ÿ"
that the size of a is now greater than 4.
I dont know what happened. Could you please give me an explanation?
Full code:
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
String(char* data)
{
setSize(0);
while (*(data + size) != '\0')
size++;
this->data = new char[size];
//need to allocate memory for 'data' pointer because 'data' pointer is now on the stack and the data must be on the heap
memcpy(this->data, data, size * sizeof(char));
}
void operator=(String rhs)
{
if (this->data != NULL)
delete[] this->data, data = NULL;
this->data = new char[rhs.getSize()]; //allocate
memcpy(this->data, data, size * sizeof(char));
}
int getSize()
{
setSize(0);
while (*(data + size))
size++;
return size;
}
void setSize(int size)
{
this->size = size;
}
void display()
{
for (int i = 0; i < size; i++)
cout << *(data + i);
}
~String()
{
if (data != NULL)
delete[] data, data = NULL;
}
private:
char* data;
int size;
};
void main()
{
String a("abcd");
String b("1");
a.display();
cout << endl;
cout << b.getSize() << endl;
a = b;
cout << a.getSize() << endl;
system("pause");
}
Whatever you're using to look at a doesn't know how much you allocated. It just knows the type.
In the first version it sees int *, so it shows a single int.
In the second version it sees char *, so it assumes it's a C string and prints whatever is in memory up to the first '\0' byte.

How can I overload = and [] operators at the same time in c++

Here I want to get rid of insert function by overloading [] and = operators.
By overloading [] operator, I have successfully returned the address of the required location where I want to insert the value.
#include<iostream>
using namespace std;
#define const maxSize = 30;
class ARRAY
{
private:
int *ar;
int end;
public:
ARRAY()
{
ar = new int[40];
end = -1;
}
void insert(int value)
{
end += 1;
ar[end] = value;
}
void insert(int value, int index)
{
if (index<30 && index >-1)
{
int tempEnd = end;
for (int i = end; i >= index; --i)
{
ar[tempEnd + 1] = ar[tempEnd];
tempEnd -= 1;
}
end += 1;
ar[index] = value;
}
else
cout << "\nOVERFLOW";
}
void remove(int index)
{
if (index >= 0 && index <= end)
{
for (int i = index; i < end; ++i){
ar[i] = ar[i + 1];
}
end -= 1;
//do something
}
else
cout << "\nNothing gonna happens";
}
void display()
{
for (int i = 0; i <=end; ++i)
cout << "\n" << ar[i];
}
int* operator[](int at)
{
if (at < 40){
end++;
return (&ar[at]);
}
}
//Here I want to do = operator overloading, How can I do this?
};
int main()
{
ARRAY arr;
arr.insert(1);
arr.insert(2);
arr.insert(3);
arr.insert(4);
arr.insert(5);
arr[5] = 10;
arr.display();
return 0;
}
You can achieve the desirable behavior by changing the return type of your operator[]:
int& operator[](int at)
In your original code it returns pointer to the array element, which, even if changed, does not do anything with the value stored in the array. With your original code, you could write something like this to change the element's value:
*(arr[5]) = 10;
which looks unobvious.
If you return reference instead of pointer, you can directly change the value it references to.
You do not need to overlaod the assignment operator '=' for your purpose. Only overload the access operator '[]'.
That should look something like this:
returntype& operator [](int indexvariable);
That is going to give you a reference to an instance of returntype and therefore you wont need to overload the assignment operator of your returntype in your case.
Then you can write:
arr[5] = 23;
You can achieve pretty much any desirable behavior with some trickery.
In your case, if you want some specific action for operator= (e.g. check bounds & insert a new element), you can introduce a helper class, that will hold a reference to an array and an index, and then overload the operator= for that class:
class ArrayIndex
{
ARRAY& array;
int index;
public:
ArrayIndex(ARRAY& a, int i) : array(a), index(i) {}
void operator=(int value)
{ array.insert(value,index); }
};
And of course you tweak your ARRAY's operator[] to return the ArrayIndex object.
If you don't want the insertion or any unusual actions, and only want to access the elements, then operator[] returning the reference (int&) will suffice.
Following the comment OP left on akexeykuzmin0's response, I suggest to alter your operator[] to return a handler to your element.
#include <iostream>
struct ARRAY
{
struct Handler
{
int* _ptr;
Handler(int* ptr) : _ptr(ptr) {}
int& operator*() { return *_ptr; }
operator int*() { return _ptr; }
int& operator=(int const& value) { *_ptr = value; return **this; }
};
int _value;
ARRAY(int n) : _value(n) {}
Handler operator[](size_t) { return Handler(&_value); }
};
int main() {
ARRAY arr(42);
std::cout
<< std::hex << &arr._value << "\n"
<< std::dec << arr._value << "\n"
<< *arr[0] << "\n\n";
arr[0] = 137;
std::cout
<< std::hex << &arr._value << "\n"
<< std::dec << arr._value << "\n"
<< *arr[0] << "\n\n";
int* pvalue = arr[0];
*pvalue = 0;
std::cout
<< std::hex << &arr._value << "\n"
<< std::dec << arr._value << "\n"
<< *arr[0] << "\n\n";
}
Output
g++ -std=c++17 -O2 -Wall -Werror main.cpp && ./a.out
0x7ffd682844f0
42
42
0x7ffd682844f0
137
137
0x7ffd682844f0
0
0
Live demo on coliru
This handler can be implicitly converted to a int* and you can overload the operator= on it.