Strange program crash - c++

I'm trying to implement a String class for an excercise. Here is my all code: (you don't need to read it all) :
#include <iostream>
using namespace std;
// ******************************* String ************************
class String {
private:
char* char_arr;
int strlen(const char* str) {
int length;
for(length=0;*(str+length)!='\0';length++);
return length;
}
void strCopy(const char* str1, char* str2) {
int length = strlen(str1);
for(int i=0;i<length;i++)
str2[i] = str1[i];
}
public:
// --- Constructor ---
String(const char* str) {
int length = strlen(str);
char_arr = new char[strlen(str)+1];
char_arr[length] = '\0';
strCopy(str,char_arr);
}
// --- size Constructor ---
explicit String(int size) {
char_arr = new char[size+1];
char_arr[size+1] = '\0';
}
// --- Destructor ---
~String() {
delete char_arr;
}
// --- Copy Constructor ---
String(const String& rhs) {
char_arr = new char[strlen(rhs.char_arr)+1];
strCopy(rhs.char_arr,char_arr);
}
// --- copy-assignment Constructor
const String& operator=(const String& rhs) {
delete char_arr;
char_arr = new char[strlen(rhs.char_arr)+1];
strCopy(rhs.char_arr,char_arr);
}
// --- operator== ---
bool operator==(const String& rhs) {
int this_length = strlen(char_arr);
int rhs_length = strlen(rhs.char_arr);
if(this_length==rhs_length) {
bool return_value = true;
for(int i=0;i<this_length;i++) {
if(char_arr[i]!=rhs.char_arr[i]) {
return_value = false;
break;
}
}
return return_value;
}
return false;
}
// --- operator+ ---
String operator+(const String& rhs) {
int this_length = strlen(char_arr);
int rhs_length = strlen(rhs.char_arr);
String new_str(this_length+rhs_length);
strCopy(char_arr,new_str.char_arr);
for(int i=0;i<rhs_length;i++) {
new_str.char_arr[i+this_length] = rhs.char_arr[i];
}
new_str.char_arr[this_length+rhs_length] = '\0';
return new_str;
}
// --- print ---
void print() {
cout << char_arr;
}
};
// ~~~~~~~ main ~~~~~~~~~
int main() {
String s = "This is";
String s1 = " My Name";
String s2 = s+s1;
s1.print();
return 0;
}
The problem is in the operator+ overloading. If you look at the main function and change the values of s and s1, the program sometimes will crash and sometimes it won't. any ideas why it's happens?

Your strCopy does not copy the trailing zero byte. While there is nothing woring with it, it is somewhat counterintuitive.
Your copy constructor uses the above strCopy() and does not add zero byte too, leaving copied string unterminated. The same applies to assignment operator.
Your concatenation operator dos not allocate enough room for trailing zero byte (but appends it). The program crashes because zero byte is placed after the allocated memory block.

Not sure this is the only problem, but you need to return *this in your const String& operator=(const String& rhs). Do enable warnings in the compiler, and it should tell you "reaching end of function without return".

Related

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

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

Implement a class want to simulate std::string but get stuck when push_back it into a vector

I have implemented a class String. When I push_back a String object to a Vector, the program gets stuck. Please help me review it if you're interested.
String.h
#pragma once
#include <iostream>
#include <memory>
#include <string>
class String {
public:
String();
String(const char *);
String(const String &);
String(const std::string &);
String(String &&) noexcept;
String &operator=(String &&) noexcept;
~String();
String &operator=(const String &);
std::string to_string() const;
friend std::ostream &operator<<(std::ostream &os, String s);
private:
std::pair<char *, char *>
alloc_n_copy(char *, char *);
void free();
static std::allocator<char> alloc;
char *beg;
char *end;
size_t length;
};
String.cpp
#include "stdafx.h"
#include <cstring>
#include "String.h"
std::allocator<char> String::alloc = std::allocator<char>();
String::String()
: beg(nullptr), end(nullptr), length(0) {}
String::String(const char *ptr) {
std::cout << "Const char constructor execute" << std::endl;
const char *tmp = ptr;
while (*tmp++ != '\0') ++length;
beg = alloc.allocate(length);
end = std::uninitialized_copy(ptr, tmp, beg);
}
String::String(const std::string &s) {
std::cout << "Const string constructor execute" << std::endl;
strcpy_s(beg, s.size(), s.c_str());
length = s.size();
char *tmp = beg;
end = tmp + length;
}
String::String(const String &s) {
std::cout << "Copy constructor execute" << std::endl;
beg = alloc.allocate(s.length);
end = std::uninitialized_copy(s.beg, s.end, beg);
}
std::pair<char *, char *>
String::alloc_n_copy(char *beg, char *end) {
auto newBeg = alloc.allocate(end - beg);
return{ newBeg, std::uninitialized_copy(beg, end, newBeg) };
}
String &String::operator=(const String &s) {
length = s.length;
auto newStr = alloc_n_copy(s.beg, s.end);
free();
beg = newStr.first;
end = newStr.second;
return *this;
}
String::String(String &&s) noexcept : beg(s.beg), end(s.end), length(s.length) {
std::cout << "Move constructor execute" << std::endl;
s.beg = s.end = nullptr;
s.length = 0;
}
String &String::operator=(String &&s) noexcept {
if (this != &s) {
beg = s.beg;
end = s.end;
length = s.length;
s.beg = s.end = nullptr;
s.length = 0;
}
return *this;
}
void String::free() {
while (length-- >= 0) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
String::~String() {
free();
}
std::string String::to_string() const {
std::string s(beg);
return s;
}
std::ostream &operator<<(std::ostream &os, String s) {
std::string str(s.beg);
os << str;
return os;
}
main.cpp
int main()
{
vector<String> v;
String s1("abc");
String s2("def");
v.push_back(s1);
v.push_back(s2);
return 0;
}
result:
Const char constructor execute
Const char constructor execute
Copy constructor execute
Move constructor execute
I don't know why the second push_back is a move construction.
And when the push_back finished, the program can't exit. Is there any resource failed to release?
Thanks
The reason that your program blocks is because your destructor never terminates:
void String::free() {
while (length-- >= 0) {
alloc.destroy(--end);
}
alloc.deallocate(beg, length);
}
Since length is an unsigned type, length >= 0 is always true. You probably don't want to be decrementing length here, before it's used as argument to alloc.deallocate(). I suggest:
void String::free() {
while (end > beg) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
There are other bugs, such as failing to initialise length before using it in the char const* constructor (I don't see why you don't just use std::strlen()) and failing to allocate in the std::string constructor. I recommend using a good set of warnings (I used g++ -std=c++2a -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++) and addressing them all. The problem above was identified very easily this way.
After compiling clear of warnings, then run your code under Valgrind or other memory checker to understand some of the outstanding issues.

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

c++ deep copy constructor [duplicate]

This question already has answers here:
Rule-of-Three becomes Rule-of-Five with C++11? [closed]
(9 answers)
Closed 5 years ago.
this is my code:
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
String& operator=(String &c){
size = strlen(c.s);
s = new char[size+1];
strcpy(s, c.s);
}
~String() { delete [] s; }// destructor
void print() { cout << s << endl; }
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
int main()
{
String str1("learnc++");
String str2 = str1;
str1.print(); // what is printed ?
str2.print();
str2.change("learnjava");
str1.print(); // what is printed now ?
str2.print();
return 0;
}
it can be compiled, and the result is:
learnc++
learnc++
learnjava
learnjava
in addition to that,there is:
*** Error in `./code': double free or corruption (fasttop): 0x0000000000f7f010 ***
BTY, if I delete "delete [] s;" in String::change,the result just becomes:
learnc++
learnc++
learnc++
learnjava
and no error appers, and why ?
the code is from geek foe feeks, I have changes some strings, and the code can be run in its IDE, but in my ubuntu 14.04, it cannot.
Your class is not following the Rule of Three because it is missing a proper copy constructor.
String str2 = str1; is just syntax sugar for String str2(str1);, so it uses the copy constructor, not your operator= (which has a memory leak, BTW).
Since you did not provide a copy constructor, the compiler provided one for you, but it does not make a deep copy of the char* data. It just copies the pointer itself, which causes the behaviors you are seeing.
A proper implementation would look more like this:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL);
String(const String &src);
~String();
String& operator=(const String &rhs);
void print() const;
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
String::String(const String &src)
{
size = src.size;
s = new char[size+1];
strcpy(s, src.s);
}
String::~String()
{
delete [] s;
}
void String::print() const
{
cout << s << endl;
}
String& String::operator=(const String &rhs)
{
if (&rhs != this)
{
String tmp(rhs);
swap(s, tmp.s);
swap(size, tmp.size);
}
return *this;
}
int main()
{
String str1("learnc++");
String str2 = str1;
str1.print();
str2.print();
//str2.change("learnjava");
str2 = "learnjava";
str1.print();
str2.print();
return 0;
}
If you are using C++11 or later, you can use this implementation instead, which follows the Rule of Five by adding move semantics:
#include <iostream>
#include <cstring>
#include <utility>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = nullptr);
String(const String &src);
String(String &&src);
~String();
String& operator=(String rhs);
void print() const;
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
String::String(const String &src)
{
size = src.size;
s = new char[size+1];
strcpy(s, src.s);
}
String::String(String &&src)
{
size = src.size;
s = src.s;
src.s = nullptr;
src.size = 0;
}
String::~String()
{
delete [] s;
}
void String::print() const
{
cout << s << endl;
}
String& String::operator=(String rhs)
{
swap(s, rhs.s);
swap(size, rhs.size);
return *this;
}
Add a copy constructor.
String(const String& c){
size = strlen(c.s);
s = new char[size+1];
strcpy(s, c.s);
}

user defined string::reserve()

So we are writing a bunch of user defined functions that mimic the string class member functions. I am stuck on reserve. It looks like it should work but I am missing something. I have tried a couple of diffrent variations such as putting this-> in front of the member varibles or my string:: in front of them... my out put does not change but throught cout statment placed within the function I know it access the function.
here is the code
#include <cstdlib>
#include <iostream>
#include <string>
#include "mystring.h"
#define string mystring
using namespace std;
void check (string s, string name){
cout << "checking " << name << endl;
cout << name << " contains " << s << endl;
cout << name << " capacity() is " << s.capacity() << endl;
cout << name << " length() is " << s.length() << endl;
cout << name << " size() is " << s.size() << endl;
cout << name << " max_size() is " << s.max_size() << endl << endl;
}
int main(int argc, char** argv) {
cout<<"This is Lab 5"<<endl;
string s1("Hello, World!");//step 2
string s1name("s1");//step 3
check(s1,s1name);//step 4
check(s1,s1name);//step 5
cout << "---Testing assignment operator---\n\n";
{
string s2;
s2=s1;
string s2name("s2");
check(s2,s2name);
if(s1==s2)
cout<<"comparison true\n";
else
cout<<"comparison false\n";
}
check(s1,s1name);
string s3("check assignment");
s3=s3;//checking to see if operator= is used when they are the same object.
check(s3,"s3");
cout<<"Lab 5 ends"<<endl;//step6
// //clear check
// s3.clear();
// check(s3,"s3");
// if(s1==s3)
// cout<<"comparison true\n";
// else
// cout<<"comparison false\n";
// reserve check
// mystring::size_type res;
// res=40;
s3.reserve(40);//still working on reserve
check(s3,"s3");
cout<<"in main buf size"<<s3.capacity()<<endl;
s3.reserve(5);
check(s3,"s3");
// char* test=s3.begin();
// cout<<&test<<endl;
// cout<<&s3<<endl;
//empty check
// string s4;
//
// if (s4.empty())
// cout<<"Empty is true\n";
// else
// cout<<"Empty is false\n";
return 0;
}
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <math.h>
#include <cstring>
using namespace std;
class mystring {
public:
// types with scope in this class
typedef unsigned int size_type;
typedef char * iterator;
typedef const char * const_iterator;
static const long int npos = 1073741824;
// default constructor
mystring();//good
// other constructors
mystring(const char *);//good
// copy constructor
mystring(const mystring& orig);//
// destructor
~mystring();////
// iterators
iterator begin();//good
iterator end();//good
//=== memory related ===
// change buffer size to n
void reserve(size_type n);
size_type size() const;////good returns len
size_type length() const;////good returns len
size_type capacity() const;////good returns buf_size
size_type max_size() const;////good
bool empty() const;////good
//=== overloading operators ===
// assignment operator
mystring& operator=(const mystring&);////
// mystring& operator=(const char *);
// array notation
char operator[](size_type pos) const;
char& operator[](size_type pos);
// append
mystring& operator+=(const mystring& str);
mystring& operator+=(const char * str);
//=== methods that modifiy the string
void clear();////good
void push_back(char c);
mystring& append(const mystring& str);
mystring& append(const char * str);
mystring& insert(size_type pos, const mystring& str);
mystring& insert(size_type pos, const char * str);
mystring& replace(size_type start, size_type span, const mystring& str);
mystring& replace(size_type start, size_type span, const char * str);
//=== conversion to c string
const char * c_str() const;//
private:
// pointer to the memory location where string is stored as a c-style
// string
char * ptr_buffer;
// the size of the memory in terms of bytes or characters capable of going into it currently
size_type buf_size;
// number of characters currently in the memory not including the
// terminating null character
size_type len;
};
#include "mystring.h"
// default constructor provided for lab 5
mystring::mystring() {
ptr_buffer = new char[1];
*ptr_buffer = '\0';
buf_size = 1;
len = 0;
}
// constructor from c-style string or "abc" provided for lab 5
mystring::mystring(const char * s) {
len = strlen(s);
buf_size = len + 1;
ptr_buffer = new char[buf_size];
strcpy(ptr_buffer, s);
}
// copy constructor to be implemented in lab 5
mystring::mystring(const mystring& orig) {
len=orig.length();
ptr_buffer=new char[len+1];
buf_size=len+1;
for(int n=0 ;n<buf_size; n++ )
{
ptr_buffer[n]=orig.ptr_buffer[n];
}
ptr_buffer[buf_size]='\0';
}
void mystring::reserve(size_type n)
{
cout<<"cccccc:"<<capacity()<<endl;
if( n > capacity() )
{
const char* temp = ptr_buffer;
ptr_buffer = new char[n];
memcpy(ptr_buffer, temp, len+1);
delete [] temp;
buf_size=n;
cout<<"bbbbbuf size"<<buf_size<<endl;
}
// char *temp;
//
// temp=new char[n];
//
// int i=0;
//
// for(;i<=len;i++)
// {
// temp[i]=ptr_buffer[i];
//
// }
// buf_size=n;
//
// delete [] ptr_buffer;
//
// ptr_buffer=temp;
//
}
mystring::iterator mystring::begin()//think is working correctly
{
iterator it=ptr_buffer;
return it;
}
mystring::iterator mystring::end()//think is working correctly
{
iterator it=ptr_buffer+len;
return it;
}
// one of the over loaded assignment operator to be implemented // assignment 3 (or for lab 5 if you have more time)
mystring& mystring::operator=(const mystring& orig){
if(this!=&orig)
// {
// cout<<"this==&mystring if statment activated\n";//comment out after testing
// break;
// }
{
delete this->ptr_buffer;
this->len=orig.len;//length();
this->ptr_buffer=new char((this->len)+1);
this->buf_size=(this->buf_size)+1;
cout<<"Using assignment operator="<<endl;
for(int n=0;n<this->buf_size;n++)
{
this->ptr_buffer[n]=orig.ptr_buffer[n];
}
this->ptr_buffer[buf_size]='\0';
return *this;
}
}
// some simple methods provided for lab 5
mystring::size_type mystring::size() const {
return len;
}
mystring::size_type mystring::length() const{
return len;
}
mystring::size_type mystring::capacity() const{
return buf_size;
}
mystring::size_type mystring::max_size() const{
return (int)pow(2,30) -4 ;
}
bool mystring::empty() const
{
if(len==0)
return true;
else
return false;
}
// destructor to free space implemented for lab 5
mystring::~mystring() {
delete [] ptr_buffer;
}
// provided for lab 5 so we may cout mystring
ostream& operator<<(ostream& out, const mystring& str) {
out << str.c_str();
return out;
}
// provided for lab 5 to support the implementation of <<
const char * mystring::c_str() const {
return ptr_buffer;
}
char mystring::operator[](size_type pos) const
{
return *(ptr_buffer+pos);
}
void mystring::clear()
{
char *temp;
temp=new char[1];
temp[0]='\0';
ptr_buffer=temp;
buf_size=0;
len=0;
}
void mystring::push_back(char c)
{
}
bool operator==(const mystring& lhs, const mystring& rhs)
{
if(lhs.length()==rhs.length())
{
for(int i=0; i<lhs.length();i++)
{
if(lhs[i]!=rhs[i])
return false;
}
return true;
}
return false;
}
So I put some cout statements in the reserve() function and in main. The reserve functions is changing the buffer size and capacity call is returning correctly but when we use the check function it is not showing that any change has occurred. I have put in revised code.
Ok, everyone. Turns out on the check function declared before main that it makes a copy of the mystring object. The copy constructor was defined to make buf_size= len + 1. So I turned it into buf_size=orig.buf_size. The reserve function probably worked as stated and yes it needed an if statment to make sure we were not shrinking it, but i was not worried about that yet. I used daves example with a little added code. thanks all for your help.
void mystring::reserve(size_type n)
{
if (n <= buf_size)
{
return;
}
from the MSDN basic_string::reserve Sets the capacity of the string to a number at least as great as a specified number.
So reserve can't be used to truncate a string
Here we are doing something stronger (we don't let the "user" shorten the buffer). Cleary you could
if (n <= len + 1) // remember the \0 terminator is not included in len
{
return;
}
and it would be correct.
But even the first form would be correct. There isn't any guarantee that reserve will "reduce" the size of a buffer.
In general, to copy between buffers, you shoud use memcpy with a length of len + 1. It's (normally) faster (no one requires it to be faster, but it is faster :-) )