C++ class objects copy constructor and operator= - c++

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;
}

Related

Assigment and adding operator overloading (concatenation of 2 tables and int)

I'm trying to concatenate two arrays and at the end concatenate int, for example: result = arg + arg + 2;
I'm getting "read access violation" at + operator overloading.
I wrote the error and warning in comments below.
My code:
Main:
#include <iostream>
#include <string>
#include "CTable.h"
int main() {
CTable c_tab1, c_tab0;
c_tab0.SetNewSize(3);
c_tab1.SetNewSize(2);
c_tab0.SetValueAt(0, 22);
c_tab0.SetValueAt(1, 23);
c_tab0.SetValueAt(2, 24);
c_tab0.Print();
c_tab1.SetValueAt(0, 31);
c_tab1.SetValueAt(1, 32);
c_tab1.Print();
CTable c_tab3 = (c_tab0 + c_tab1 + 111);
c_tab3.Print();
return 0;
}
Class CTable:
#include <iostream>
#include <string>
using namespace std;
class CTable {
public:
CTable();
CTable(string sName, int iTableLen);
CTable(const CTable& pcOther);
CTable* pcClone();
~CTable();
void ShowName();
void ShowSize();
void SetName(string sName);
bool SetNewSize(int iTableLen);
void SetValueAt(int iOffset, int iNewVal);
void Print();
CTable& operator+(const CTable& pcNewTable);
CTable operator+(int iNewVal) const;
CTable& operator=(const CTable& pcNewVal) {
if (this != &pcNewVal) {
for (int i = 0; i < i_size; i++) {
this->piTable[i] = pcNewVal.piTable[i];
}
}
return *this;
}
private:
string s_name;
int i_size;
int* piTable;
const int SIZE = 10;
const string NAME = "Name";
};
#include <iostream>
#include <string>
#include "CTable.h"
#include <algorithm>
using namespace std;
CTable::CTable() {
s_name = NAME;
cout << "bezp: " << s_name << endl;
piTable = new int[SIZE];
i_size = SIZE;
}
CTable::CTable(string sName, int iTableLen) {
s_name = sName;
cout << "parametr: " << sName << endl;
piTable = new int[iTableLen];
i_size = iTableLen;
}
CTable::CTable(const CTable& pcOther) {
s_name = pcOther.s_name + "copied";
piTable = new int[pcOther.i_size];
i_size = pcOther.i_size;
for (int i = 0; i < pcOther.i_size; i++) {
piTable[i] = pcOther.piTable[i];
}
}
CTable::~CTable() {
delete[] piTable;
}
void CTable::SetName(string sName) {
s_name = sName;
}
bool CTable::SetNewSize(int iTableLen) {
if (iTableLen <= 0) {
cout << "Length has to be greater than 0" << endl;
return false;
}
int* pi_newTable = new int[iTableLen];
for (int i = 0; i < iTableLen; i++) {
pi_newTable[i] = piTable[i];
}
delete this->piTable;
this->i_size = iTableLen;
this->piTable = pi_newTable;
return true;
}
CTable* CTable::pcClone() {
CTable* ct = new CTable(s_name, i_size);
return ct;
}
void CTable::ShowName() {
cout << "Name: " << s_name << endl;
}
void CTable::ShowSize() {
cout << "Size: " << i_size << endl;
}
void CTable::SetValueAt(int iOffset, int iNewVal) {
if (iOffset >= this->i_size) {
return;
}
piTable[iOffset] = iNewVal;
}
void CTable::Print() {
for (int i = 0; i < i_size; i++) {
cout << piTable[i] << " ";
}
cout << endl;
}
CTable& CTable::operator+(const CTable& pcNewTable) {
CTable result("new_int", this->i_size);
result.i_size = (i_size + pcNewTable.i_size);
result.piTable = new int[i_size + pcNewTable.i_size];
for (int i = 0; i < i_size; i++) {
result.piTable[i] = piTable[i];
}
for (int i = 0; i < (pcNewTable.i_size); i++) {
result.piTable[i+i_size] = pcNewTable.piTable[i];
}
return result; //Warning C4172 returning address of local variable or temporary: result
}
CTable CTable::operator+(int iNewVal) const {
CTable result("new_int", this->i_size);
result.i_size = (i_size + 1);
result.piTable = new int[i_size + 1];
for (int i = 0; i < i_size; i++) {
result.piTable[i] = piTable[i]; //Exception thrown: read access violation. **this->piTable** was 0x1110122.
}
result.piTable[i_size + 1] = iNewVal;
return result;
}
What should I correct? I'm not sure about assigment operator overload, is it okay?
The member function SetNewSize has undefined behavior. In this loop
int* pi_newTable = new int[iTableLen];
for (int i = 0; i < iTableLen; i++) {
pi_newTable[i] = piTable[i];
}
it 1) uses uninitialized values because the array was not initialized and 2) iTableLen can be gretaer than the current value of i_size. You should at least zero initialize the array in constructors.
The copy assignment operator aslo has undefined behabior because the number of elements of the array of the object pcNewVal can be less than the number of elements of the array in the assigned object.
The first overloaded operator + also have undefined behavior. For starters as the warning says the operator returns reference to the local object result that will not be alive after exiting the operator. Secondly, there is a memory leak necause the array of the object is allocated anew and the previuous allocated memory in the constructor is not freed.
CTable result("new_int", this->i_size);
result.i_size = (i_size + pcNewTable.i_size);
result.piTable = new int[i_size + pcNewTable.i_size];
//...
The second overloaded operator + also has undefined behavior. As in the previous operator there is a memory leak.
In this statement
result.piTable[i_size + 1] = iNewVal;
there is an access memory outside the allocated array. There should be
result.piTable[i_size] = iNewVal;

Compilation error appears because of missing const?

I am trying to run a code that defines objects that are a collection of English letters.
I dont know why it does not compile.
I have tried to change from int to const int but it is not the case,
and also added the disable 4996 message but it didnt help.
#include <iostream>
using namespace std;
class CharSet
{
int size;
char* pSet;
public:
// -----------------------------------
CharSet(int const size, char* set)
{
this->size = size;
pSet = new char[strlen(set) + 1];
strcpy(pSet, set);
}
// -----------------------------------
~CharSet()
{
delete[] pSet;
}
// -----------------------------------
CharSet operator*(const CharSet & other)
{
int maxSize = 0;
if (this->size >= other.size)
maxSize = this->size;
else
maxSize = other.size;
char * ret = new char[maxSize + 1];
char temp;
int index = 0;
for (int i = 0; i < this->size; i++)
{
temp = this->pSet[i];
for (int j = 0; j < other.size; j++)
{
if (other.pSet[j] == temp)
{
ret[index] = temp;
index++;
}
}
}
ret[index] = '\0';
return CharSet(maxSize, ret);
}
// -----------------------------------
bool operator()(char check)
{
bool flag = false;
for (int i = 0; i < this->size; i++)
{
if (pSet[i] == check)
flag = true;
}
return flag;
}
// -----------------------------------
friend ostream& operator<<(ostream& os, const CharSet& s)
{
os << s.pSet;
return os;
}
// -----------------------------------
};
int main()
{
CharSet s1(4, "DAQP"), s2(3, "AXZ");
cout << s1 * s2 << endl;
if (s1('Q') == true)
cout << "The element is member of the set" << endl;
else
cout << "The element is not member of the set" << endl;
return 0;
}
errors:
E0289 no instance of constructor "CharSet::CharSet" matches the argument
E0289 no instance of constructor "CharSet::CharSet" matches the argument list
C4996 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
C2664 'CharSet::CharSet(const CharSet &)': cannot convert argument 2 from
C2664 'CharSet::CharSet(const CharSet &)': cannot convert argument 2 from 'const char [4]' to 'char *'
you need a const char* in your constructor:
CharSet(int const size, const char* set)
Thanks to
#holy black cat
"DAQP" is a const char[] which you didn't provide a constructor for that(the array will implicitly convert to pointer).
A better way is using std::string:
class CharSet
{
std::string pSet;
public:
// -----------------------------------
CharSet(std::string set) : pSet(set)
{
}
// -----------------------------------
~CharSet()
{
}
// -----------------------------------
CharSet operator*(const CharSet & other)
{
int maxSize = 0;
std::string ret;
char temp;
int index = 0;
for (int i = 0; i < pSet.size(); i++)
{
temp = pSet[i];
for (int j = 0; j < other.pSet.size(); j++)
{
if (other.pSet[j] == temp)
{
ret += temp;
index++;
}
}
}
return CharSet(ret);
}
// the rest of members ...
//
};
full code at godblot

Getting and setting values to an array with Overloading the subscript operator “[ ]” won't work

After really hard search for answers...
I tried fo(u)r hours to get and set values to an array with Overloading the subscript operator “[ ]” but can't figure out why it won't work.
What I'm tring to do here is to set someType value to an array member (On Main "darr1[i] = i*10.0" for example) with overloading the [] and with overloading the = and to get someType value from an array member (On Main "<< darr1[i] << endl" for example) but can't figure out why just the overloading of: "Type & operator [] (int index)" is invoking.
My program doesn't get to the '=' overloading or to the second '[]' overloading at all..
here is my program (sorry for the long one):
#include <iostream>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class AO1Array
{
private:
int _size;
protected:
int top;
int *B;
int *C;
AO1Array(int n);
~AO1Array();
bool isRealValue(int index)
{
if ((0 <= B[index] && B[index] < top) && (index == C[B[index]]))
return true;
return false;
};
};
AO1Array::AO1Array(int n)
{
_size = n;
top = 0;
B = new int[n];
C = new int[n];
}
AO1Array::~AO1Array()
{
delete[] B;
B = NULL;
delete[] C;
C = NULL;
}
template<class Type>
class GenericO1Array : AO1Array
{
public:
GenericO1Array(int size, Type initVal) : AO1Array(size)
{
_initVal = initVal;
Len = size;
A = new Type[size];
}
~GenericO1Array()
{
delete[] A;
A = NULL;
}
int Length() { return Len; }
Type & operator [](int index) const
{
if (AO1Array::isRealValue(index))
return A[index];
return _initVal;
}
Type & operator [] (int index)
{
if (AO1Array::isRealValue(index))
realValue = true;
else
realValue = false;
return A[index];
}
Type operator =(Type value)
{
if (realValue)
A[lastIndex] = _initVal;
else
{
AO1Array::C[top] = lastIndex;
AO1Array::B[lastIndex] = AO1Array::top++;
A[index] = value;
}
return *this;
}
private:
int Len;
int lastIndex;
bool realValue;
Type _initVal;
Type *A;
};
int main()
{
int n = 20;
GenericO1Array<double> darr1(n, 1.1);
GenericO1Array<long> iarr1(n, 2);
int i;
cout << "\nLength.darr1 = " << darr1.Length() << endl;
cout << "\nLength.iarr1 = " << iarr1.Length() << endl;
for (i = 0; i < n; i += 2)
{
darr1[i] = i*10.0;
iarr1[i] = i * 100;
} // for
cout << "\ndarr1 = " << endl;
for (i = 0; i < n; i++)
cout << "darr1[" << i << "] = " << darr1[i] << endl;
cout << "\niarr1 = " << endl;
for (i = 0; i < n; i++)
cout << "iarr1[" << i << "] = " << iarr1[i] << endl;
} // main
My program doesn't get to the '=' overloading
You are overloading the = assignment operator of Generic01Array itself, but nothing in your code is actually assigning values to your darr1 or iarr1 variables directly (there are no darr1 = ... or iarr = ... statements). That is why your = operator is not being invoked.
If you want something to happen when the user assigns a value to an element of your array, you need to create a proxy class and overload its = assignment operator, then have your [] operator return an instance of that proxy:
template<class Type>
class GenericO1Array : AO1Array
{
public:
class Proxy;
friend Proxy;
class Proxy
{
private:
Generic01Array& _arr;
int _index;
public:
Proxy(Generic01Array &arr, int index) : _arr(arr), _index(index) {}
operator Type() const
{
if (_arr.isRealValue(index))
_arr.realValue = true;
else
_arr.realValue = false;
return _arr.A[_index];
}
Proxy& operator=(const Type &value)
{
if (_arr.realValue)
_arr.A[_arr.lastindex] = _arr._initVal;
else
{
_arr.C[_arr.top] = _arr.lastIndex;
_arr.B[_arr.lastIndex] = _arr.top++;
_arr.A[_index] = value;
}
return *this;
}
};
...
Proxy operator [] (int index)
{
return Proxy(*this, index);
}
...
};
or to the second '[]' overloading at all..
You have two overloads of the [] operator, one that is const and the other is not. The const version of [] is breaking the const-ness of the operator by returning a non-const reference to the array's internal data. It should be returning a non-reference const value instead:
const Type operator [](int index) const
The non-const version of the [] operator can return a reference:
Type& operator [](int index)
You are not calling the [] operator on any const instances of your Generic01Array class, so only the non-const version of your [] operator should be getting invoked.

C++: Passing a (pointer to an?) array of objects by reference

I'm new to C++, and I'm having significant trouble with creating an array of objects using a pass by pointer and reference. This is not the actual code; it's an example of what the code essentially does.
#include <iostream>
class MyClass
{
public:
MyClass();
static int doStuff(MyClass *&classArray);
void print_number();
private:
int number;
};
MyClass::MyClass()
{
}
int MyClass::doStuff(MyClass *&classArray)
{
int i = 0;
for (i = 0; i < 10; i++) {
*classArray[i].number = i;
}
return i;
}
void MyClass::print_number()
{
std::cout << number << "\n";
}
int main(void)
{
MyClass *test = nullptr;
int p = MyClass::doStuff(test);
std::cout << p << '\n';
for (int i = 0; i < 10; i++) {
test[i].print_number();
}
return 0;
}
When compiled, this gives a segmentation fault.
This is how you do it (don't forget do delete classArray with delete[] at the end of your program or destructor:
new operator has to have default constructor, if you want to use non-default it is easier to create copy constructor, then a temporary object and copy.
#include <iostream>
class MyClass
{
public:
MyClass();
MyClass(int x, int y);
MyClass(MyClass &OldClass);
static int doStuff(MyClass *&classArray, int Size, int x, int y);
void print_number();
private:
int number, x, y;
};
MyClass::MyClass()
{
number = 0;
x = 0;
y = 0;
}
MyClass::MyClass(int x, int y)
{
number = 0;
this->x = x;
this->y = y;
}
MyClass::MyClass(MyClass &OldClass)
{
this->number = OldClass.number;
this->x = OldClass.x;
this->y = OldClass.y;
}
int MyClass::doStuff(MyClass *&classArray, int Size, int x, int y)
{
if (Size > 0)
{
classArray = new MyClass[Size];
for (int i = 0; i < Size; i++)
{
classArray[i] = MyClass(x, y);
classArray[i].number = i;
}
return Size;
}
else
return 0;
}
void MyClass::print_number()
{
std::cout << number << " " << x << " " << y << "\n";
}
int main(void)
{
MyClass *test = nullptr;
int p = MyClass::doStuff(test, 10, 5, 6);
std::cout << p << '\n';
for (int i = 0; i < p; i++) {
test[i].print_number();
}
delete[] test;
std::cin.get();
return 0;
}
It is not working because you need to allocate the array, as the function is trying to access elements of an array which has yet not been initialized to hold that amount of elements. You can do this by
MyClass *test = new MyClass[array_size];
Or
MyClass test[array_size];
Or by using a resizable container such as std::vector, and changing the function parameters accordingly
*classArray[i].number = i;
You called doStuff with a null pointer, so classArray is null and is not an array. Dereferencing a null pointer results in undefined behavior and on most implementations you'll usually get a crash.
You're also dereferencing something that's not a pointer so this code will not even compile. The error I get is:
main.cpp:23:9: error: indirection requires pointer operand ('int' invalid)
*classArray[i].number = i;
^~~~~~~~~~~~~~~~~~~~~
Presumably this is just because, as you say, the code you're showing is not your real code and classArray[i].number corresponds to a pointer in your real code. But I thought I'd point this out anyway, just in case.
Given the context of your code, here's a working example of your code:
#include <iostream>
class MyClass
{
public:
MyClass() {}
static int doStuff(MyClass*& classArray, size_t sz)
{
int i = 0;
for (; i < sz; i++) {
classArray[i].number = i;
}
// not sure what you want here, but this will return sz+1 if sz>0
return i;
}
void print_number()
{
std::cout << this->number << std::endl;
}
private:
int number;
};
int main(void)
{
MyClass* test = new MyClass[10];
int p = MyClass::doStuff(test, 10);
std::cout << p << '\n';
for (int i = 0; i < 10; i++) {
test[i].print_number();
}
delete[] test;
return 0;
}
Though as others have pointed out, you are using C++, while it's a great exercise in understand how to pass pointers and arrays around, you might find the STL and C++stdlib contain a lot of these types of idioms in an 'easier to understand context'.
Here's your code with some C++ STL:
#include <iostream>
#include <vector>
class MyClass
{
public:
MyClass() {}
MyClass(int i) : number(i) {}
static int doStuff(std::vector<MyClass>& classArray, size_t sz)
{
int i = 0;
for (; i < sz; i++) {
classArray.push_back(MyClass(i));
}
// not sure what you want here, but this will return sz+1 if sz>0
return i;
}
void print_number()
{
std::cout << this->number << std::endl;
}
private:
int number;
};
int main(void)
{
std::vector<MyClass> test;
int p = MyClass::doStuff(test, 10);
std::cout << test.size() << '\n';
// can use iterators here if you want
std::vector<MyClass>::iterator itr = test.begin();
for (; itr != test.end(); itr++) {
itr->print_number();
}
return 0;
}
Hope that can help.

Dynamic Memory Allocation for Objects

#include <iostream>
class MyClass
{
public:
MyClass() {
itsAge = 1;
itsWeight = 5;
}
~MyClass() {}
int GetAge() const { return itsAge; }
int GetWeight() const { return itsWeight; }
void SetAge(int age) { itsAge = age; }
private:
int itsAge;
int itsWeight;
};
int main()
{
MyClass * myObject[50]; // define array of objects...define the type as the object
int i;
MyClass * objectPointer;
for (i = 0; i < 50; i++)
{
objectPointer = new MyClass;
objectPointer->SetAge(2*i + 1);
myObject[i] = objectPointer;
}
for (i = 0; i < 50; i++)
std::cout << "#" << i + 1 << ": " << myObject[i]->GetAge() << std::endl;
for (i = 0; i < 50; i++)
{
delete myObject[i];
myObject[i] = NULL;
}
I am wondering why the objectPointer must be inside the for loop, if I take it out and place it right before the for loop, I get nonsensical results. Help would be appreciated, thanks...sorry for the terrible formatting.
myObject[i] = objectPointer;
It should be inside the loop because you are storing a new reference in the array of the pointers. If it is outside the loop, then all the array of pointers point to the same reference. In such scenario, you should be careful while deallocation as all the array of pointers point to the same memory location.