why subtracting from pointer in this destructor and assignoperator? - c++

Hi I have a test tomarrow and can't figure out why subtraction is made on the pointer before checking if the refcount is 0. I've been searching on google but still cant figure it out. So I'm hoping turning to you guys :) would help.
Easyiest is too just show you the code, I've marked the lines with comments, so here it is:
This is the class StringRep that has pointers to it for counting pointerref to it,
struct StringRep{
int size; // amount of chars incl. EOL \0-tecken
char* chars; // Pointer to char
int refCount; // Amount of String-variables
};
And this is class String that uses the StringRep,
class String{
public:
String(char* str);
String(const String& other);
~String();
const String& operator=(const String& rhs);
char get(int index) const { return srep->chars[index]; }
void put(char ch, int index);
private:
StringRep* srep;
};
String::String(const String& other):srep(other.srep){
srep->refCount++;
}
String::~String(){
if (--srep->refCount == 0){ //why --srep here?
delete [] srep->chars;
delete srep;
}
}
const String& String::operator=(const String& rhs){
if (srep != rhs.srep){
if (--srep->refCount == 0){ //why --srep here?
delete [] srep->chars;
delete srep;
}
srep = rhs.srep;
srep->refCount++;
}
return *this;
}
void String::put(char ch, int index){
if (srep->refCount > 1){ //Why not --srep here?
StringRep* tmpRep = new StringRep;
tmpRep->refCount = 1;
tmpRep->size = srep->size;
tmpRep->chars = new char[tmpRep->size];
std::strcpy(tmpRep->chars, srep->chars);
--srep->refCount;
srep = tmpRep;
}
srep->chars[index] = ch;
}
This is all info I have on the example question for the test, I know that --spek points to the object before spek, but cant figure out the logic behing checking if what is pointed at before now is 0 then its okey to delete, or to copy, but why? As I said I've searched the webb and have found some answers to help me understand the functions of the pointer and the subtraction etc, it more the logic that is confusing.
Best regards

Because of operator precendence, --srep->refCount is not decrementing srep, but the refCount member.
So, the code is decrementing the refCount, and if it comes down to 0, it can assume that the last reference to the object is being destroyed.

--srep->refCount
is parsed as
--(srep->refCount)
because prefix decrement has lower priority than -> (however, postfix decrement has the same priority as ->). Always use parens in your own code!

Related

Is there a better way to initalize pointers in c++?

Below I have a bit of code for implementing Huffman compression.
What I was curious about was if I could initialize the left and right pointers without including cstdlib, or rather, if I could initialize an empty memory location for storing the left and right without using malloc.
Also, in my combine function, I do not want to use "NULL" for the string parent node of my left and right, but would rather have an empty string. Will I have to make a new constructor for this? I get an error (basic_string::_M_construct null not valid) when I replace "NULL" with nullptr.
#include <string>
#include <cstdlib>
#ifndef PRIORITY_NODE
#define PRIORITY_NODE
namespace Huffman
{
class PriorityNode
{
private:
std::string key; // The character sequence to compress
long frequency = 0; // The frequency of the character sequence
PriorityNode* left = (PriorityNode*)malloc(sizeof(PriorityNode));
PriorityNode* right = (PriorityNode*)malloc(sizeof(PriorityNode));
public:
PriorityNode(std::string k, long f): frequency(f), key(k){};
std::string getKey() const{ return key;}
long getFrequency() const{ return frequency;}
void setLeft(const PriorityNode& left){*this->left = left;}
void setRight(const PriorityNode& right){*this->right = right;}
PriorityNode& getLeft() const{ return *left;}
PriorityNode& getRight() const{ return *right;}
friend PriorityNode combine(const PriorityNode& lhs, const PriorityNode& rhs)
{
long ret_freq = lhs.getFrequency() + rhs.getFrequency();
PriorityNode ret = PriorityNode("NULL", ret_freq);
ret.setLeft(lhs);
ret.setRight(rhs);
return ret;
};
};
}
#endif
So a couple of points.
key is a string, not a pointer. It makes no sense to set it to nullptr, i am guessing "NULL", is just a stand in for when the key has no value. Instead of this, just use an empty string "".
You should try to avoid this manual memory management for several reasons. First of all you have no destructor, so your memory that you malloc is never freeed meaning you have a memory leak. Secondly, you allocate memory for the sub nodes, even if you do not need to. I would suggest something more like the following:
class PriorityNode {
private:
...
std::shared_ptr<PriorityNode> left, right; // Default constructs to nullptr
...
friend PriorityNode combine(const std::shared_ptr<PriorityNode> lhs,
const std::shared_ptr<PriorityNode> rhs) {
PriorityNode ret = PriorityNode("", ret_freq);
ret.setLeft(lhs);
ret.setRight(rhs);
return ret;
};
The empty string is "". You can pass that to the existing constructor. std::string doesn't distinguish empty strings and NULL pointers like C strings. It is not allowed to initialize an std::string with a NULL pointer. If you need a separate null state, use std::optional<std::string>.

definition of "operator +=" behaving unexpectedly

Brief
I am defining my own String class. Everything works fine except for my definition of += which I intend to use to concatenate Strings
//expected behaviour
String c = "foo";
String d = "lala";
c+=d;
cout<<c;
Should output:
foolala
I am having an issue with it as it does seem to work correctly except for the last bit where it seems to not be passing the pointer.
Here is the code (I have omitted most of the other definitions as I don't think they are useful for this)
Code
class String{
private:
unsigned int SizeS;
char *Buffer;
public:
String():SizeS(0){}
String(unsigned int i):SizeS(i){Buffer=new char[SizeS];}
String(const char *string)
{
//defines the initialiser
SizeS = strlen(string); //find out the length of the string
Buffer = new char[SizeS]; //allocate space for the entire string+1 for terminator
memcpy(Buffer,string,SizeS); //copy to buffer the whole thing
Buffer[SizeS]=0; //terminate the buffer with an end character
}
char * GetBuffer() const { return this->Buffer; }
String (const String& copied) :SizeS(copied.SizeS)
{
// defines how copying works
Buffer = new char[SizeS];
memcpy(Buffer,copied.Buffer,SizeS);
}
// this is where the issue is ------------------
String* operator += (const String& to_concat)
{
unsigned int newSize = this->SizeS + to_concat.SizeS;
String *p = new String(newSize) ;
memcpy(p->Buffer,this->Buffer,this->SizeS);
memcpy(p->Buffer+this->SizeS,to_concat.Buffer,to_concat.SizeS);
std::cout<<p->Buffer<<std::endl;
return p;
}
// this is where the issue ends ------------------
};
std::ostream& operator<< (std::ostream& stream, const String& other) { stream << other.GetBuffer(); return stream; }
int main()
{
String c="foo";
std::cout<<c<<std::endl;
c += c;
std::cout<<c<<std::endl;
}
Expected Output
foo
foofoo
foofoo
Actual Output
foo
foofoo
foo
Question
What am I doing wrong? From my understanding, I am overwritting the pointer c with the pointer p, but it seems as though c does not change. Why is that ?
Solution
After reading the comments and suggestion I came up with this solution which works.
String& operator += (const String& to_concat)
{
unsigned int newSize = this->SizeS + to_concat.SizeS;
char* p = new char[newSize];
memcpy(p,this->Buffer,this->SizeS);
memcpy(p+this->SizeS,to_concat.Buffer,to_concat.SizeS);
delete[](this->Buffer);
this->Buffer=p;
this->SizeS=newSize;
return *this;
}
Because you didn't write any code to change c.
There is no "pointer c" and, even if there were, you'd not be overwriting it.
Your += creates a new, dynamically allocated string, using the data from the original two strings, then returns a pointer to it which your program then throws away (incidentally, leaking that new string).
Instead of creating and returning p, you should be modifying the buffer in this (then, conventionally, returning *this as a String& to permit chaining).
Also, a += operator should not produce output.
A + operator could work similarly to how you've done it, since they're supposed to produce new objects, but you shouldn't actually use new for that — you'd still have a memory leak. Try to avoid dynamic allocation (though you're going to need to dynamically allocate the buffer that each String's buffer pointer point to).

Trouble with Operator Overloading and the *this Pointer

I'm working on operator overloading for a self made dynamic array class. I'm also trying to learn how to use the *this pointer but it's not going so well. Below are the parts of the class and my code that I thought were needed to explain the issue.
I don't understand why I can't call a member function on the *this pointer when the *this pointer is pointing to the left side of the + equation.
Here is the stub driver that is calling the + operator:
<< and >> are already overloaded and working.
cout << "Please enter a word to add:";
string theWord;
cin >> theWord;
//add word
array1 = array1 + theWord;
cout << "array1: " << array1 << endl;
Here is the main code:
class DynamicArray
{
public:
//constructor
DynamicArray(int initialcapacity = 10);
//copy constructor
DynamicArray(const DynamicArray& rhs);
//destructor
~DynamicArray();
//operator+ - add a string
DynamicArray operator+(const string& rhs) const;
//operator+ - concatenate another DynamicArray
DynamicArray operator+(const DynamicArray& rhs) const;
//change the capacity of the DynamicArray to the newCapacity -
// may reduce the size of the array - entries past newCapacity will be lost
void resize(int newCapacity);
private:
string* mWords;//pointer to dynamic array of strings
int mNumWords;//the current number of words being kept in the dynamic array
int mCapacity;//the current capacity of the dynamic array (how many strings could fit in the array)
//display all the contained strings (each on a newline) to the output stream provided
void displayContents(ostream& output) const;
//add all the strings contained in the input stream to the dynamic array - resize if necessary
//return how many words are added to the array
int addWords(ifstream &input);
//add a single word to the dynamic array - resize if necessary
void addWord(const string& word);
};
//add a single word to the dynamic array - resize if necessary
void DynamicArray::addWord(const string& word)
{
if (mNumWords >= mCapacity)//need more space?
{
resize(mCapacity + 1);
}
mWords[mNumWords] = word;
mNumWords++;
}
This is the function I'm currently working on
//operator+ - add a string
DynamicArray DynamicArray::operator+(const string& rhs) const
{
//this doesn't work, why doesn't it, how should/do I use the
//this pointer properly
this.addWord(rhs);
return *this;
}
In addition to using this. instead of this->, you have defined operator+ as a const member function. This means that no members can be mutated, and no calls to non-const member function functions are to be performed. Your addword function is non-const. That's the reason for the error -- you are violating const correctness.
Also, why are you mutating the array for merely calling operator + in your code? That operator shouldn't need to change any aspect of the current object. I can understand operator += mutating the object, but not operator +.
What you should do is write an operator += first. This operator can mutate the current object, since that is the semantics of += (change the current object). Then operator + can be written this way:
//operator+ - add a string
DynamicArray DynamicArray::operator+(const string& rhs) const
{
DynamicArray temp = *this;
temp += rhs;
return temp;
}
This is assuming you have a working copy constructor. In the example above, you are mutating a temporary, calling += on the temporary, and returning the temporary. The this object does not change.
Here is what your operator += should look like:
DynamicArray& DynamicArray::operator+=(const string& rhs)
{
this.addWord(rhs);
return *this;
}
Now operator += becomes non-const because the intention of += is to change the current object. Note that the return value is a reference to the current object. This now works in concert with the operator + above.
Your operator+ has to be non-const since you want to change the state of the object on each it is invoked. Also you don't have to write
this->addWord( rhs); // correct but not needed
because use of method inside a class member function is called on this pointer implicitly. Given this, you may write:
DynamicArray DynamicArray::operator+(const string& rhs)
{
addWord( rhs);
return *this;
}
You can also implement this operator as void:
void DynamicArray::operator+(const string& rhs)
{
addWord( rhs);
}
and use it this way:
array1 + theWord;

Array Wrapper class in C++

So, I'm new to C++ (brand new), and as an assignment I have to write a class that acts as an array wrapper. Since I'm so new, I'm unsure whether my overloaded operators and such work, so if anyone could be so kind as to tell me how my code looks:
This would be the header:
class MyArray
{
private:
string* sList;
unsigned int size;
public:
MyArray(const unsigned int size = 1);
MyArray(const MyArray &toCopy);
MyArray& operator=(const MyArray& toAssign);
~MyArray();
//MyArray& operator+
string& operator[](const int index);
const int size();
};
And this would be the underlying code:
MyArray::MyArray(const unsigned int initSize)
: size(initSize)
{
sList = new string[initSize];
return;
}
MyArray::MyArray(const MyArray &toCopy)
: size(toCopy.size)
{
if(toCopy.sList)
{
sList = new string[size];
for(int a=0; a<size; a++){
strcpy(sList[a], toCopy.sList[a]);
}
}
else sList = NULL;
return;
}
MyArray& operator=(const MyArray& toAssign)
{
if(this != &toAssign)
{
if(sList)
{
delete [] sList;
}
size = toAssign.size;
if (toAssign.sList)
{
sList = new string[size];
for(int a=0; a<size; a++){
strcpy(sList[a], toCopy.sList[a]);
}
}
else
{
sList = NULL
}
}
}
MyArray::~MyArray()
{
delete [] sList;
return;
}
string& MyArray::operator[](const int index)
{
return sList[index];
}
const int MyArray::size()
{
return this.size;
}
The operator+ function still needs to be written, but I want to make sure what I have makes sense before I proceed.
How do you want your code (i.e. the class you are authoring) to be used by other programmers (including you)?
Write an example program to demonstrate the use of your class.
An example program serves as a rudimentary test set. You can start as the following.
int main() {
MyArray arr1( 5 );
MyArray arr2( arr1 );
}
Have you thought about how user code will put (string?) elements into the MyArray class?
There are couple of other issues with the current version of the code. That is okay to begin with, but it is important for you to learn to test your own code; you need to learn the skill where you have some basic confidence in your own code (not necessarily perfect code) because you cannot always ask somebody else to test your code.
Suggestion: Since you mentioned that you are new, I would suggest you to build a array wrapper class for int's first. This is because, managing strings has some extra challenges than managing ints :-). Once you do that, you can easily do it for strings.
There is a naming conflict between size and size()
Copy C++ strings using =, not strcpy (which is for char*)
Missing MyArray:: in definition of operator=
toCopy should be toAssign in operator=
Missing semicolon after sList = NULL
Missing return *this; at the end of operator=
In defintion of size(), this.size should be size, this->size or (*this).size
All of these mistakes will be discovered by a compiler (you may need to enable warnings for the missing return to be reported; on g++ use the -Wall flag). It is just a matter of understanding the compiler's error messages and knowing how to fix the problems.

C++: How would one implement their own String class?

I'm trying to re-learn C++ and was wondering if anyone could help me out here. I'm trying to implement my own String class to see if I can remember things, but I'm stuck on the constructor.
I have my header file and want to have a constructor as so:
Header File (MyFiles\String.h):
#ifndef STRING_
#define STRING_
using namespace std;
#include <iostream>
class String
{
private:
static const unsigned int MAX = 32; // Capacity of string
char Mem[MAX]; // Memory to hold characters in string
unsigned Len; // Number of characters in string
public:
// Construct empty string
//
String()
{
Len = 0;
}
// Reset string to empty
//
void reset()
{
Len = 0;
}
// Return status information
//
bool empty() const
{
return Len == 0;
}
unsigned length() const
{
return Len;
}
// Return reference to element I
//
char& operator[]( unsigned I )
{
return Mem[I];
}
// Return constant reference to element I
//
const char& operator[]( unsigned I ) const
{
return Mem[I];
}
// Construct string by copying existing string
//
String( const String& );
// Construct string by copying array of characters
//
String( const char [] );
// Copy string to the current string
//
String& operator=( const String& );
// Append string to the current string
//
String& operator+=( const String& );
};
// Compare two strings
//
bool operator==( const String&, const String& );
bool operator!=( const String&, const String& );
// Put a string into an output stream
//
ostream& operator<<( ostream&, const String& );
#endif
The bit I'm stuck on is this:
String::String(const String& str)
{
//what goes here?
}
Thanks!
Well, since it's a learning exercise.
I think you want to copy the contents of the other string here since this is a copy constructor. So you will want to copy across all the member variables. In your case
the copy constructor is not necessary because you've got a static array. If you had
dynamic memory (i.e. used new to allocate pointer to Mem) then you'd need this. However,
to show you how it's done, here you go.
String::String(const String& str)
{
//what goes here?
assert(str.Len < MAX); // Hope this doesn't happen.
memcpy(Mem, str.Mem, str.Len);
Len = str.Len;
}
You need to copy the data from str to this. The length is easy:
Len = str.Len; // or, equiv. this->Len= str.Len
The data is a little harder. You might use strcpy or memcpy, or even a for loop.
memcpy(Mem, str.Mem, sizeof Mem);
Good luck!
I concur with Kornel Kisielewicz: the fewer hand-rolled String classes, the better. But you're only doing this to learn, so fair enough :-). Anyway: your copy constructor needs to copy over the length and the contents of the Mem array, and that's it.
(If you were doing this to make something useful rather than as a learning exercise, I'd add: a string class with a fixed maximum string length -- especially one as small as 32 characters -- is a very bad idea indeed. But it's entirely reasonable if you don't feel like dealing with memory allocation and deallocation at the same time as you're trying to remember the even-more-basics...)