Why do I keep getting a segmentation fault with my delete function - c++

I have an assignment where I need to use an overloaded destructor to delete dynamically allocated pointers. However, when it runs, some of my pointers are deleted until a segmentation fault with one of my objects' pointers the one pointing to " and the second" made with a parameterized constructor.
I have tried to go through and make sure the delete operator had brackets (because my new operator did) I made sure the object still existed by printing out its information and address. I have tried to rewrite my allocation function, and I have tried to go over my destructor to see where it messes up.
If it helps, I have included my destructor, my allocation function, my deallocation function, and my parameterized constructor.
'''
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
void MyString::buffer_deallocate() {
cout << m_buffer << endl;
delete[](m_buffer);
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
};
'''
Every time however I get the output after "and the second" to be
Segmentation fault (core dumped)
Edit: I have tried the majority of what was recommended. At least what I understood, the problem still persists, and I realize now I was kind of sparse on my code. (Please forgive me, I'm still learning.) Here is the new code along with the rest of the function file for reference:
'''
#include<iostream>
#include<string.h>
using namespace std;
#include"MyString.h"
//Default Constructor
MyString::MyString()
:m_size(0), m_buffer(NULL)
{
buffer_allocate(0);
};
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(strlen(str)+1), m_buffer(NULL)
{
buffer_allocate(m_size);
strncpy(m_buffer, str, m_size);
};
//Copy Constructor
MyString::MyString(const MyString & other)
:m_size(0), m_buffer(NULL)
{
const char * otherPtr = other.c_str();
buffer_allocate(other.size());
for(int i = 0; i < size(); i++)
{
m_buffer[i] = otherPtr[i];
}
m_buffer[m_size] = '\0';
};
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
size_t MyString::size() const
{
return m_size;
}
size_t MyString::length() const{
return m_size-1;
}
const char * MyString::c_str() const{
return m_buffer;
}
bool MyString::operator==(const MyString & other) const {
char * m_bufferPointer = m_buffer;
while(*m_bufferPointer++)
{
const char * str_ptr = other.c_str();
if(*m_buffer != *str_ptr++)
{
return 0;
}
}
return 1;
}
MyString & MyString::operator=(const MyString & rhs) {
buffer_deallocate();
buffer_allocate(rhs.size());
const char * c_strPtr = rhs.c_str();
int i;
for(i = 0; i < rhs.size(); i++)
{
this->m_buffer[i] = c_strPtr[i];
}
return *this;
}
MyString MyString::operator+ (const MyString & other_myStr) const {
char * temp_pointer;
temp_pointer;
size_t temp_size = m_size + other_myStr.size();
//New Combined Buffer for Concatanation
try {
temp_pointer = new char[temp_size];
temp_pointer = strcat(this->m_buffer, other_myStr.c_str());
}
catch(bad_alloc&)
{
cout << "Error: Unable to Allocate Memory";
return NULL;
}
return MyString(temp_pointer);
}
char & MyString:: operator[](size_t index) {
return m_buffer[index];
}
const char & MyString::operator[] (size_t index) const {
return m_buffer[index];
}
ostream & operator << (ostream& os, const MyString & myStr) {
os << myStr.m_buffer;
return os;
}
void MyString::buffer_deallocate() {
cout << "Trying to delete : " <<m_buffer << endl;
if(m_buffer){
delete[](m_buffer);
}
cout << " Success" <<endl;
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
m_size = 0;
}
}
'''

in MyString::buffer_deallocate
cout << m_buffer << endl;
requires that m_buffer be null-terminated. Unfortunately, MyString::MyString(const char * str) does not make this guarantee.
You could
for(int i = 0; i < m_size; i++)
{
cout << m_buffer[i] << endl;
}
to print the string out character by character instead, but it is probably more useful to waste a byte, null terminate, and take advantage of the Standard library
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
m_buffer[m_size] = '\0'; // add the null
}
and then
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size+1]; // +1 for the null terminator
m_size = size;
}
catch(bad_alloc&) // this is a bad thing to do here. More on that later.
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
But we can simplify this with a few library function calls.
MyString::MyString(const char * str)
:m_size(strlen(str))
{
buffer_allocate(m_size);
strcpy(m_buffer, str);
}
Addendum:
Your class may be violating the Rule of Three. If MyString does not have a copy constructor and an assignment operator to go along with the destructor any copies, deliberate or accidental, will turn a MyString into a time bomb. The destructor of one of the copies will be run before the others leaving the others without a valid m_buffer.
MyString::buffer_allocate cannot safely return void unless it allows the exception to propagate. Catching the bad_alloc leaves the object without a valid allocation at m_buffer and the rest of the program will not know this. Either every other access in the program must test for a valid buffer or engage in undefined behaviour trying to access the invalid memory. It's likely better to let the exception fall through and be caught by another part of the program that is better suited to making a decision on what to do.
MyString::buffer_allocate will leak an existing allocation if called on a MyString that already has a valid allocation at m_buffer. I recommend a
if (m_buffer)
{
delete[] m_buffer;
}
and initializing m_buffer to null in MyString's constructor
MyString(const char* str)
: m_size(std::strlen(str)), m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}

So I had to remake this to some working code.
The problem might be that a string length must be extended with 1 to add the nul-termination. So that makes:
// prevent some MSVS warnings->errors
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
//Destructor
class MyString {
private:
size_t m_size;
char* m_buffer;
void buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch (std::bad_alloc&)
{
std::cout << "Errror: Unable to allocate memory\n";
// allocation failed, nothing changed, don't delete/
m_size = 0;
}
}
void buffer_deallocate() {
// printing in the buffer deallocation??
delete[] m_buffer;
m_buffer = nullptr;
m_size = 0;
}
public:
MyString(const char* str)
: m_size(std::strlen(str)+1) // add null termination
, m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}
~MyString() {
buffer_deallocate();
}
void Print() const {
std::cout << m_buffer << '\n';
}
};
int main() {
std::string input{ "Hello World!" };
MyString myString(input.c_str());
myString.Print();
}

Related

initializing char* a = new char[size] does not work

I have a problem about initializing char* a = new char[size]. Here is my code.
class Practice
{
public:
Practice(const char* a);
~Practice();
const char* getString() const;
private:
char* mString;
int mSize;
};
#include "Practice.h"
Practice::Practice(const char * a)
:mSize(0)
,mString(nullptr)
{
while (a[mSize] != '\0')
{
mSize++;
}
mString = new char[mSize];
for (int i = 0; i < mSize; i++)
{
mString[i] = a[i];
}
}
Practice::~Practice()
{
delete[] mString;
}
const char* Practice::getString() const
{
return mString;
}
int main()
{
Practice p("Hello");
std::cout << p.getString() << std::endl;
return 0;
}
I expected the result is Hello.
But the result was like Hello²²²²▌▌▌▌▌▌▌■a%{▌.
I thought I initialized the mString member variable through mString = new char[mSize]. But it was not working the way I thought.
Can anybody enlighten me what's wrong with my code and fix it?
mString is not NUL-terminated.
Your're checking a constructor parameter for \0. But you are not allocating mString to put the same \0 there, and don't copy \0 from a.
So, resulting mString is not properly NULL-terminated and it would be read beyond end.
Reading beyond end of allocated is undefined behavior. In particular case, it is likely that either mString would output until some accidental zero, or crash would occur.
while (a[mSize] != '\0')
{
mSize++;
}
This sets mSize equal to the length of a, excluding the terminating '\0'. You should include the terminator in your copy:
while (a[mSize++])
{
}
or simply:
mSize = strlen(a) + 1;
You are not null-terminating your mString data, but it is expecting to be null-terminated when you pass it to std::cout. Without that terminator, std::cout reads into surrounding memory until it encounters a random null byte (or crashes with a read access error). That is why you are seeing std::cout output random garbage after your data.
You are also not following the Rule of 3/5/0, as you are missing a default constructor, copy and move constructors, and copy and move assignment operators.
Try this:
class Practice
{
public:
Practice(const char* a = nullptr);
Practice(const Practice &src);
Practice(Practice &&src);
~Practice();
Practice& operator=(Practice src);
const char* getString() const;
private:
char* mString;
int mSize;
};
#include "Practice.h"
#include <utility>
Practice::Practice(const char * a)
: mSize(0)
, mString(nullptr)
{
if (a)
{
while (a[mSize] != '\0')
{
++mSize;
}
}
mString = new char[mSize + 1];
for (int i = 0; i < mSize; ++i)
{
mString[i] = a[i];
}
mString[mSize] = '\0';
}
Practice::Practice(const Practice &src)
: mSize(src.mSize)
, mString(nullptr)
{
mString = new char[mSize + 1];
for (int i = 0; i < mSize; ++i)
{
mString[i] = src.mString[i];
}
mString[mSize] = '\0';
}
Practice::Practice(Practice &&src)
: mSize(src.mSize)
, mString(src.mString)
{
src.mString = nullptr;
src.mSize = 0;
}
Practice::~Practice()
{
delete[] mString;
}
Practice& Practice::operator=(Practice src)
{
std::swap(mString, src.mString);
std::swap(mSize, src.mSize);
return *this;
}
const char* Practice::getString() const
{
return mString;
}
#include <iostream>
#include "Practice.h"
int main()
{
Practice p("Hello");
std::cout << p.getString() << std::endl;
return 0;
}

Assertion `str' failed

I am new bee for c++ . I got follow error when I override operator+ .
ConsoleApplication1.out: /root/projects/ConsoleApplication1/sdstring.cpp:43: static sdstring::size_t sdstring::strlen(const char*): Assertion `str' failed.
This is my test code!
sdstring sd1(NULL);
cout << "sd1:" << sd1 << endl;
sdstring sd2("sd2");
cout << "sd2:" << sd2 << endl;
sdstring sd3;
cin >> sd3;
cout << "sd3:" << sd3 << endl;
sd3 +=sd2 ;
cout << "sd3:" << sd3 << endl;
sdstring sd4 =sd3+sd1;
cout << "sd4:" << sd2 << endl;
sd1 = sd2 + sd1;
cout << "sd1:" << sd1 << endl;
cout << "sd3==sd2:" << (sd3 == sd2)<<endl;
Error happened in This line .
sdstring sd4 =sd3+sd1;
This is my sdstring.cpp file.
#include "sdstring.h"
#include <assert.h>
sdstring::sdstring(const char *str) {
if (!str) {
datas = new char[1];
datas[0] = '\0';
}
else {
datas = new char[strlen(str)+1];
strcpy(datas, str);
}
}
sdstring::sdstring(const sdstring& str) {
datas = new char[strlen(str.datas) + 1];
strcpy(datas, str.datas);
}
sdstring& sdstring::operator+(const sdstring& str)const {
sdstring result(NULL);
size_t total_size = getlen() + str.getlen();
result.datas = new char[total_size + 1];
strcpy(result.datas, datas);
strcat(result.datas, str.datas);
return result;
}
bool sdstring::operator==(const sdstring& str)const {
return strcmp(datas, str.datas) == 0;
}
sdstring& sdstring::operator=(const sdstring& str) {
if (this == &str)
return *this;
delete[] datas;
datas = new char[str.getlen() + 1];
strcpy(datas, str.datas);
return *this;
}
sdstring::size_t sdstring::strlen(const char* str) {
assert(str);
size_t len = 0;
while ('\0' != *str++)
len++;
return len;
}
char* sdstring::strcpy( char* des, const char* src){
assert(des&& src);
char* temp = des;
while ('\0' != (*des++ = *src++));
return temp;
}
int sdstring::strcmp(const char* fir, const char* sec) {
assert(fir && sec);
while (*fir == *sec)
{
if (*fir == '\0') {
return 0;
}
++fir;
++sec;
}
return *fir - *sec;
}
char* sdstring::strcat(char* des,const char* src) {
char* temp = des;
while ('\0' != *des)
{
des++;
}
while ('\0' != (*des++ = *src++));
return temp;
}
sdstring::~sdstring()
{
if (datas)
{
delete[] datas;
datas = nullptr;
}
}
char& sdstring::operator[](const unsigned int position)const
{
return position < getlen() ? datas[position] : datas[position-1];
}
sdstring& sdstring::operator+=(const sdstring& str)
{
size_t total_size = getlen() + str.getlen();
if (total_size != getlen()) {
char* temp = datas;
datas = new char[total_size + 1];
strcpy(datas,temp);
strcat(datas, str.datas);
delete[] temp;
}
return *this;
}
ostream& operator<<(ostream& os, const sdstring& str)
{
os << str.datas;
return os;
}
istream& operator>>(istream& is, sdstring& str)
{
char* cache = new char[1024];
is >> cache;
delete[]str.datas;
str.datas = new char[sdstring::strlen(cache)];
sdstring::strcpy(str.datas, cache);
delete[]cache;
return is;
}
Who can help me , Thanks for first!
There are several things wrong with your code. However the operator + is wrong in that it is returning a reference to a local variable, which is undefined behavior.
sdstring& sdstring::operator+(const sdstring& str)const
{
sdstring result(NULL);
//..
return result; // Undefined behavior.
}
operator + should be returning a brand new object, not a reference to an object.
sdstring sdstring::operator+(const sdstring& str)const // <-- Note the return value is sdstring
{
sdstring result(NULL);
//..
return result;
}
The other issues with your code:
1) The operator= destroys the memory using delete[] datas; before calling new[] to allocate memory for the new string. If new[] throws an exception, the sdstring object would be corrupted since the data has been destroyed and you can't go back and reset the string with the old data. Use the copy / swap idiom to prevent this from happening.
2) You should store the length of the string in a member variable instead of calling strlen every time you want to know the length of the string. The strlen is slow in that it has to loop to count every single character to determine where the terminating '\0' character is located. Instead of that, just store the length of the string once and keep using that value.
3) Instead of writing your own strlen and strcmp functions, use the library functions strlen and strcmp. Your versions are not optimized, unlike the library versions.
4) operator + can be written in terms of operator +=. operator + should be written very simply as:
sdstring sdstring::operator + (const sdstring& rhs)
{
return sdstring(*this) += rhs;
}

Trying to write my own string class getting exception in gcc

Basically what the title says. I have been trying to write my own string class using only char arrays and, while my code works when I run it in Visual Studio, I have trouble with it when using gcc .The problem seems to be coming when i try to delete in my getData function(can be seen below) The exception I get is:
Exception thrown at 0x6262436B (ucrtbased.dll) in string.exe: 0xC0000005: Access violation reading location 0xCCCCCCBC. occurred
My code :
Header:
#pragma warning(disable:4996)
#ifndef STRING_STRING_H
#define STRING_STRING_H
#include<iostream>
#include<cstring>
#include<fstream>
class String {
private:
char *data; //holds the text
size_t maxSize; //maximum number of chars in data
size_t currentSize; //current number of chars in data
void getData(const char *, size_t maxSize); //sets currentSize to the other char* size and
// copies the content of the other char* to data
public:
String(); //default constructor
~String(); //destructor
String(const String &); //copy-constructor(from String)
String(const char *); //copy-constructor(from char*)
String operator=(const String &); //operator= (from string)
String operator=(const char *); //operator=(from char*)
size_t length() const; //currentSize getter
void addChar(const char); //adds a char to the data array
void getLine(std::ifstream&,const char); // reads line till deliminator and stores it in this string object(all data previously stored is lost)
size_t find(const char*); //searches for text in the string and if found returns the starting position , if not found returns -1;
void print() const; //prints the string object to console
char* toChar() const; //returns a new allocated char pointer with the text inside (must be deleted afterwards)
};
#endif //STRING_STRING_H
cpp:
#include "String.h"
String::String() {
currentSize = 0;
maxSize = 16;
try {
data = new char[maxSize];
data[0] = '\0';
}
catch (std::bad_alloc &) {
std::cerr << "Not enough memory" << std::endl;
throw;
}
}
String::~String() {
delete[] data;
}
size_t String::length() const {
return currentSize;
}
String::String(const String &other) {
this->maxSize = other.maxSize;
getData(other.data, maxSize);
}
String::String(const char *other) {
this->maxSize = strlen(other) *2;
getData(other, maxSize);
}
void String::getData(const char *dataSource, size_t maxSize) {
currentSize = strlen(dataSource);
try {
char *newData = new char[maxSize];
delete[] data;
data = newData;
strcpy(data, dataSource);
}
catch (std::bad_alloc &) {
std::cerr << "Not enough memory" << std::endl;
throw;
}
}
String String::operator=(const String &other) {
if (this != &other) {
maxSize = other.maxSize;
getData(other.data, maxSize);
}
return *this;
}
String String::operator=(const char *other) {
if (this->data != other) {
maxSize = strlen(other) *2;
getData(other, maxSize);
}
return *this;
}
void String::addChar(const char newChar) {
if (maxSize == currentSize+1) {
maxSize *= 2;
getData(this->data, maxSize);
}
data[currentSize++] = newChar;
}
void String::getLine(std::ifstream & is, const char delim='\n')
{
char temp;
while (!is.eof())
{
is.get(temp);
if (temp == delim)
break;
else
addChar(temp);
}
return;
}
size_t String::find(const char * text)
{
size_t currPos=-1;
bool found = 0;
for (size_t i = 0; i < currentSize; i++)
{
if (data[i] == text[0])
{
for (size_t j = i+1; j < currentSize; j++)
{
if (data[j] == text[j - i])
found = 1;
else
{
found = 0;
break;
}
}
if (found == 1)
{
currPos = i;
break;
}
}
}
return currPos;
}
void String::print() const
{
for (size_t i = 0; i < currentSize; i++)
{
std::cout << data[i];
}
std::cout << std::endl;
}
char * String::toChar() const
{
char* text= new char[currentSize+1];
for (size_t i = 0; i < currentSize; i++)
{
text[i] = data[i];
}
text[currentSize + 1] = 0;
return text;
}
Your problem is caused by calling delete [] on uninitialized memory.
In the copy constructor, the data member is not initialized before calling getData(). In getData(), you are using delete [] data;.
You could initialize data to nullptr in the constructors to avoid the problem.
It's always a good idea to initialize all variables to some sensible value before the body of the constructor. E.g. you can implement the copy constructor as:
String::String(const String &other) : currentSize(0),
maxSize(other.maxSize),
data(nullptr)
{
getData(other.data, maxSize);
}
Similarly, implement the constructor from char const* as:
String::String(const char *other) : currentSize(0),
maxSize(strlen(other) *2),
data(nullptr)
{
getData(other, maxSize);
}

Read Access Violation _First was nullPtr

I have a default constructor, copy constructor, destructor, assignment operator, length, and string functions. When I call the copy constructor and try to print the value of the new char*, I get a Read Access Violation _First was nullptr. The debugger shows iosfwd code and breaks at the error, but I have no clue what this means. Any help would be awesome.
This is where the debugger shows the error.
static size_t __CLRCALL_OR_CDECL length(const _Elem *_First)
{ // find length of null-terminated string
return (*_First == 0 ? 0 // <- this line
: _CSTD strlen(_First));
}
These are my functions (also not that we were not allowed to use STRCPY)
MyString::MyString() { //default constructor
string = new char[6];
int i = 0;
for (i; i < strlen(string); i++) {
string[i] = NULL;
}
string[i] = '\0';
}
MyString::MyString(const MyString &s) { //copy constructor
char* string = new char[strlen(s.string) + 1];
int i = 0;
for (i; i < strlen(s.string); i++) {
string[i] = s.string[i];
}
string[i] = '\0';
}
MyString::~MyString() { //destructor
delete[] string;
string = NULL;
}
MyString& MyString::operator=(const MyString& s) { //assignment operator
char* temp = new char[strlen(s.string) + 1];
int i = 0;
for (i; i < strlen(s.string); i++) {
temp[i] = s.string[i];
}
temp [i] = '\0';
delete[] string;
string = temp;
return *this;
}
size_t MyString::length() const { //length of string
return strlen(string);
}
char* MyString::cString() { //string
return string;
}
int main(int argc, const char * argv[]){
MyString s;
std::cout << "Empty: '" << s.cString() << "'\n";
s = "hello";
std::cout << "After operator=: '" << s.cString() << "'\n";
{
MyString t(s);
std::cout << "After copy: '" << t.cString() << "'\n";
s = t;
}
}
This line in your copy constructor
char* string = new char[strlen(s.string) + 1];
Creates a new local variable called string which means that your member variable called string is never assigned a value.
Remove the char *

_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) after "return 0"

I'm writing a program where I have to write my own string class and a potion class. I know that this run-time error is usually caused when the program is deleting something that wasn't allocated, but it occurs after the 'return 0' in main. I've debugged it line-by-line and tried some things to try and eliminate the run-time error, but nothing has really worked. Is there anybody that can help me out with this?
Here is my code:
//Main
#include "Potion.h"
#include <iostream>
using std::cout;
using std::endl;
void TotalCost( Potion ArrayofPotions[5] )
{
String Currency[4] = {"Platinum", "Gold", "Silver" ,"Copper"};
int TotalCost[4] = {0, 0, 0, 0};
int Cost[4] = {0, 0, 0, 0};
for (short i = 0; i < 5; i++)
{
for (short k = 0; k < 4; k++)
{
Cost[k] = ArrayofPotions[i].ConvertCost(k);
TotalCost[k] += Cost[k];
if ( i != 0 )
TotalCost[k] = ArrayofPotions[k].CalculateCost(TotalCost[k - 1], TotalCost[k]);
}
}
cout << "\nTotal cost for all potions: ";
for (short i = 0; i < 4; i++)
{
cout << TotalCost[i] << ' ';
Currency[i].Display();
}
}
int main()
{
Potion Haggling("Potion of Haggling", "You haggle for 10% better prices for 30 seconds",
"Weak", "0.80.0.4.");
Potion Strength("Draught of Strength", "You can carry 20% more for 300 seconds",
"Low", "2.60.5.1.");
Potion Health("Solution of Health", "You are 30% tougher for 60 seconds",
"Mild", "2.20.5.1.");
Potion Stealth("Philter of Stealth", "You are 40% harder to detect for 60 seconds",
"Moderate", "0.90.5.1.");
Potion Waterbreathing("Elixir of Waterbreathing", "You can breathe underwater for 60 seconds",
"Strong", "2.10.5.0.");
Potion ArrayOfPotions[5] = {Haggling, Strength, Health, Stealth, Waterbreathing};
for (short i = 0; i < 5; i++)
ArrayOfPotions[i].DisplayPotions();
TotalCost(ArrayOfPotions);
system("pause");
return 0;
}
//String class
class String
{
public:
String() : m_str(nullptr)
{ }
String(char chr) : m_str(nullptr)
{
m_str = new char;
*m_str = ch;
}
String(char * str)
{
if (str != nullptr)
{
m_str = new char[strlen(str) + 1];
strcpy(m_str, str);
}
}
String(const String & copy) : m_str(copy.m_str)
{ }
String& operator=(const String & rhs)
{
if ( this != &rhs )
{
delete [] m_str;
m_str = new char[strlen(rhs.m_str) + 1];
strcpy(m_str, rhs.m_str);
}
return *this;
}
~String()
{
delete [] m_str;
}
void Display() const
{
cout << m_str;
}
char * GetStr() const
{
return m_str;
}
String Upper()
{
char * check = this->m_str;
for (unsigned short i = 0; i < strlen( this->m_str ); i++, check++)
{
if ( *check > 96 && *check < 123 )
{
*check -= 32;
}
else
m_str = this->m_str;
}
return m_str;
}
private:
char * m_str;
};
//Potion class
#include "String.h"
class Potion
{
public:
Potion() : m_name(nullptr), m_description(nullptr), m_potency(nullptr),
m_cost(nullptr)
{ }
Potion(String name, String description, String potency, String cost)
{
m_name = name;
m_description = description;
m_potency = potency.Upper();
m_cost = cost;
}
Potion(const Potion & copy) : m_name(copy.m_name), m_description(copy.m_description),
m_potency(copy.m_potency), m_cost(copy.m_cost)
{ }
int ConvertCost(int index)
{
int cost = 0;
char * new_cost = m_cost.GetStr();
char * temp_string = new char[strlen( new_cost ) + 1];
strcpy(temp_string, new_cost);
char * temp = strtok( temp_string, "." );
for (short k = 0; k != index; k++)
temp = strtok( NULL, "." );
cost = atoi( temp );
delete [] temp_string;
return cost;
}
int CalculateCost(int & cost1, int cost2)
{
if (cost > 99)
{
cost1++;
cost2 -= 100;
}
return cost2;
}
void DisplayPotions()
{
String Currency[4] = {"Platinum", "Gold", "Silver" ,"Copper"};
int cost = 0;
m_name.Display();
m_description.Display();
m_potency.Display();
for (short i = 0; i < 4; i++)
{
cost = ConvertCost(i);
if (cost != 0)
{
cout << ConvertCost(i) << ' ';
Currency[i].Display();
}
}
}
private:
String m_name;
String m_description;
String m_potency;
String m_cost;
};
I'm sorry it's really long and I left out some of the output formatting, but I really need some help with this.
P.S. My professor wants us to convert the cost from a string to an int and it has to be put in that "0.0.0.0" format. That's why it is written like that.
Your assignment operator is broken, as it copies the pointer from the right hand side.
You need to follow the same procedure as in the copy constructor.
By the way, the copy constructor is also broken, as it will fail if rhs is empty.
(It's a safer idea to use an empty string in your representation of an empty string (i.e. use "" instead of nullptr).
It saves you from a lot of null checking.)
This constructor is also broken:
String(char * str)
{
if (str != nullptr)
{
m_str = new char[strlen(str) + 1];
strcpy(m_str, str);
}
}
as it leaves m_str uninitialised if you pass it nullptr - and you do that a lot.
This constructor is also broken:
String(char chr) : m_str(nullptr)
{
m_str = new char;
*m_str = ch;
}
as it doesn't construct a zero-terminated string.
You need
m_str = new char[2];
m_str[0] = ch;
m_str[1] = 0;
As others stated, your constructors are broken. In addition to this, the assignment operator is also broken in a subtle way.
The easiest way to fix this is move most of the assignment operator code to the copy constructor.
String(const String & copy) : m_str(new char[strlen(copy.m_str)+1])
{
strcpy(m_str, copy.m_str);
}
As to your assignment operator, the flaw is it deletes the memory before new[] is called. What happens if new[] throws an exception? You've destroyed your old data and you have no way to recover the data.
Instead of how you wrote the assignment operator, a better way is this:
#include <algorithm>
//...
String& operator=(String rhs)
{
std::swap(rhs.m_str, m_str);
return *this;
}
That was simple, and it works. It requires a working copy constructor, and working destructor for String. Since you provided these two functions, writing the assignment operator becomes trivial.
See the copy/swap idiom: What is the copy-and-swap idiom?
This also ensures that if new[] throws an exception, your original string is not destroyed. The std::swap merely swaps the two items -- if you can't use std::swap then write your own function to swap the pointers, as it should be very simple to implement.