I build a simple string class.
I try to make the concatenation functions which one of them is + and the other +=.
When trying to implement += I generate a Str object under that equals the 1st string and which size is s.size(). But then when I tried to append to it a new string t I need to free the old array s string and allocate new size for it. After the destructor of the temp Str object is called it stucks there when freeing the old space and I can't understand why. How can I deallocate the Str under the + member function?
class Str
{
public:
typedef size_t size_type;
typedef char* iterator;
typedef const char* const_iterator;
iterator begin(){ return p; }
iterator end() { return p + std::strlen(p); }
const_iterator begin() const { return p; }
const_iterator end() const { return p + std::strlen(p); }
size_type size() const { return data_length; }
Str() {};
Str(const Str& s):
p(new char[s.size() +1]),
data_length(s.size())
{
std::copy(s.begin(), s.end(), p);
p[data_length] = '\0';
}
Str(const char* cp) :
p(new char[std::strlen(cp) + 1 ]),
data_length(std::strlen(cp))
{
std::copy(cp, cp+ std::strlen(cp) + 1,p);//copies also the '\0' char to the last place in p
}
Str& operator=(Str& rhs)//assignment operator
{
if (&rhs != this)
{
uncreate();
create(rhs.size());
std::copy(rhs.begin(), rhs.end() + 1, p);
//p[rhs.size()] = '\0';
}
return *this;
}
Str& operator=(const char* cp)//assignment operator
{
if (cp!= p)
{
uncreate();
create(std::strlen(cp));
std::copy(cp, cp+std::strlen(cp), p);
p[data_length] = '\0';
}
return *this;
}
Str& operator+=(const Str&);
~Str()
{
delete[] p;//stucked here while returning from + member function
data_length = 0;
}
const char* c_str() const;
void copy(char* ,size_type);
private:
char* p;
size_type data_length = 0;
const_iterator ci() const { return p; }
void uncreate();
void create(size_type);
};
Str operator+(const Str& s, const Str& t)
{
Str r = s;
r += t;
return r;
}
inline Str& Str::operator+=(const Str &s)
{
//trying to allocate new space for this object
std::copy(s.begin(),s.end(),p+this->size());
p[data_length] = '\0';
return *this;
}
void Str::create(Str::size_type n)
{
p = new char[n + 1];
data_length = n;
}
void Str::uncreate()
{
delete[] p;//to check that p is allocated right
data_length = 0;
}
The main for example:
int main()
{
Str s1 = "hello";
Str s2 = "worly";
Str s3 = s1 + s2;
return 0;
}
I suppose you want something like this:
inline Str& Str::operator+=(const Str &s)
{
const int new_data_length = data_length + s.data_length;
char * temp = new char[new_data_length + 1];
memcpy(temp, p, data_length);
memcpy(temp + data_length, s.p, s.data_length);
delete [] p;
p = temp;
data_length = new_data_length;
p[data_length] = 0;
return *this;
}
Related
I am trying to write a basic string class for practice, and I am struggling with the reserve() function to increase my char array's memory allocation.
This is the error message I get:
free(): double free detected in tcache 2
This is my approach:
void string::reserve(size_t n)
{
if (n <= space+1) return; // never decrease allocation
char *p = new char[n+1]; // allocate new space
for (int i=0; i<sz; ++i) p[i]=pChars[i]; // copy old elements
//strcpy(p, pChars); // didn't work either
p[n] = '\0';
delete[] pChars; // deallocate old space
pChars = p;
space = n+1;
}
I will also share a picture of my header file below:
Here is the entire code:
#include "String.h"
namespace String
{
string::string(): sz(0), pChars(nullptr), space(0) {}
string::string(const char* s): sz(strlen(s)), pChars(new char[strlen(s)+1]), space(sz+1) ///Constructor using a null-terminated C String
{
//for (int i = 0; s[i] != '\0'; i++) pChars[i] = s[i];
strcpy(pChars,s);
pChars[strlen(s)] = '\0';
}
string::string(const string& s): sz(s.sz), pChars(new char[s.sz]), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}
string& string::operator=(const string& s) ///copy assignment
{
if (this==&s) return *this; // self-assignment, no work needed
if (s.sz<=space)
{ // enough space, no need for new allocation
for (size_t i = 0; i<s.sz; ++i) pChars[i] = s.pChars[i]; // copy elements
sz = s.sz;
return *this;
}
char *p = new char [s.sz]; // copy and swap
for (size_t i = 0; i<s.sz; ++i) p[i] = s.pChars[i];
delete[] pChars;
sz = s.sz;
space = s.sz;
pChars = p;
return *this;
}
string& string:: operator=(const char* s) ///copy assignment using a null-terminated C String
{
char *p = new char[strlen(s)+1];
for(size_t i = 0; s[i] != '\0'; i++) p[i] = s[i];
p[sz] = '\0';
delete [] pChars;
sz = strlen(s);
space = sz;
strcpy(pChars, p);
return *this;
}
///string(string&& s); ///move constructor, not needed (compiler copy elision)
string& string:: operator=(string&& s) ///move assignment
{
delete[] pChars; // deallocate old space
pChars = s.pChars; // copy a's elem and sz
sz = s.sz;
space = s.space;
s.pChars = nullptr; // make a the empty vector
s.sz = 0;
cout << "Move" << endl;
return *this;
}
string::~string()
{
delete [] pChars;
}
char& string:: operator[](size_t pos) {return pChars[pos];} ///set operator
const char& string:: operator[](size_t pos) const {return pChars[pos];} ///get operator
void string::reserve(size_t n)
{
if (n <= space+1) return; // never decrease allocation
char *p = new char[n+1]; // allocate new space
for (int i=0; i<sz; ++i) p[i]=pChars[i]; // copy old elements
//strcpy(p, pChars);
p[sz] = '\0';
delete[] pChars; // deallocate old space
pChars = p;
space = n+1;
}
void string:: push_back(const char c) ///push char c to the end
{
if (sz==0) // no space: grab some
reserve(8);
else if (sz==space) // no more free space: get more space
reserve(2*space);
pChars[sz] = c; // add d at end
++sz; // and increase the size (sz is the number of elements)
}
bool operator==(const string& lhs, const string& rhs) ///check equality (same size, all chars the same)
{
if (lhs.size() != rhs.size()) return false;
else
{
for (size_t i = 0; i<lhs.size(); i++)
{
if (lhs[i] != rhs[i]) return false;
}
}
return true;
}
bool operator==(const string& lhs, const char* rhs) ///equality with null terminated C string
{
if(lhs.size() == strlen(rhs))
{
for (size_t i = 0; i< lhs.size(); i++)
{
if(lhs[i] == rhs[i]) return true;
}
}
return false;
}
bool operator>(const string& lhs, const string& rhs) ///greater than operator, true if lhs > rhs
{
}
string operator+(const string& lhs, const string& rhs) ///concatenate two MyStrings, return the result
{
}
ostream& operator<<(ostream& os, const string& s)
{
for (size_t i = 0; i<s.size(); i++)
{
os << s[i];
}
return os;
}
istream& operator>>(istream& is, string& s)
{
char c;
while(is.get(c))
{
if (c!=' ') s.push_back(c);
}
return is;
}
}
First error
string::string(const string& s): sz(s.sz), pChars(new char[s.sz]), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}
This allocates an array of s.sz chars and then writes off the end of it
SHould be
string::string(const string& s): sz(s.sz), pChars(new char[s.sz] + 1), space(s.sz) ///copy constructor
{
for (size_t i = 0 ; i<s.sz; i++) pChars[i] = s.pChars[i];
pChars[s.sz] = '\0';
}
For this project we are suppose to cover the topic of dynamic memory allocation and deallocation. The functionality of the 'operator<<; is to output(to terminal or file depending on the type of ostream& os object passed as a parameter to it) the MyString data (the C-string representation held within m_buffer). Right after my program prints to the Terminal "Access Successful." I receive a Segmentation fault (core dumped) I am quite sure that it have something to do with the operator << but I do not know how to fix this. Hence, it fails to print "Testing Operator <<()". Any Suggestions? Thank you!
//int main
int main(){
std::cout << "Testing operator[]()" << std::endl;
MyString ms_access("Access successful (NOT)");
ms_access[17] = 0;
std::cout << "Testing operator<<()" << std::endl;
cout << ms_access << endl;
}
class MyString{
public:
MyString();
MyString(const char * str);
MyString(const MyString & other_myStr);
~MyString();
size_t size() const;
size_t length() const;
const char * c_str() const;
bool operator== (const MyString & other_myStr) const;
MyString & operator= (const MyString & other_myStr);
MyString operator+ (const MyString & other_myStr) const;
char & operator[] (size_t index);
const char & operator[] (size_t index) const;
friend std::ostream & operator<<(std::ostream & os, const MyString & myStr);
private:
void buffer_deallocate();
void buffer_allocate(size_t size);
char * m_buffer;
size_t m_size;
};
//definition
std::ostream& operator<<(std::ostream& os, const MyString& myStr){
//if(os == std::cout){
os << myStr.m_buffer << std::endl;
//}
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar arrayof a non-constqualified object.
char& MyString::operator[](size_t index){
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar array of a constqualified object.
const char& MyString::operator[](size_t index)const{
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
void MyString::buffer_deallocate(){
if(m_buffer != NULL){
delete [] m_buffer;
}
}
void MyString::buffer_allocate(size_t size){
if(m_buffer != NULL){
buffer_deallocate();
}
m_size = size;
m_buffer = new char[m_size];
}
MyString::MyString(){
m_size = 0;
m_buffer = NULL;
}
MyString::MyString(const char *str){
m_buffer = NULL;
m_size = strlen(str);
buffer_allocate(m_size);
strcpy(m_buffer,str);
}
MyString::MyString(const MyString &other){
m_buffer = NULL;
m_size = other.m_size;
buffer_allocate(m_size);
strcpy(m_buffer,other.m_buffer);
}
size_t MyString::size()const{
return m_size;
}
size_t MyString::length(){
return strlen(m_buffer)-1;
}
const char* MyString::c_str() const {
char *str = NULL;
str = new char[m_size];
for(size_t i = 0; i < m_size;i++){
str[i] = *(m_buffer+i);
}
return str;
delete [] str;
str = NULL;
}
bool MyString::operator==(const MyString & other)const{
if(strcmp(m_buffer,other.m_buffer) == 0){
return true;
}else if (strcmp(m_buffer,other.m_buffer) != 0){
return false;
}
}
MyString & MyString::operator=(const MyString & str1){
buffer_deallocate();
m_buffer = new char[str1.m_size];
strcpy(m_buffer,str1.m_buffer);
}
MyString MyString::operator+(const MyString & other_myStr)const{
MyString myStr(strcat(m_buffer,other_myStr.m_buffer));
return myStr;
}
Following up on https://codereview.stackexchange.com/q/126242/23788.
I wrote my string class and according to the feedback I have changed some stuff. Is there anything more that should be fixed?
+operator doesn't work and I do not know what I've done wrong. I have a segfault when I do "Str+Str".
Process finished with exit code 139
And this is my Str.h
class Str {
friend std::istream &operator>>(std::istream &, Str &);
friend void swap(Str &s, Str &t) {
std::swap(s.data, t.data);
std::swap(s.length, t.length);
std::swap(s.alloc, t.alloc);
}
public:
typedef char *iterator;
typedef size_t size_type;
Str() : data(nullptr), length(0), capacity(0) { }
Str(size_type length, char char_to_fill) : Str() { create(length, char_to_fill); }
Str(const char *s) : Str() { create(s); }
template<class In>
Str(In b, In e) : Str() { create(b, e); }
~Str() {
if (data) alloc.deallocate(data, capacity);
data = nullptr;
}
Str(const Str &s) {
*this = s;
}
// move constructor?
Str(Str &&other)
: Str() {// initialize via default constructor, C++11 only
swap(*this, other);
}
Str &operator+=(const Str &s) {
size_type new_length = length + s.length - 1; //remove 1 because of 2 nulls
if (new_length > capacity) {
reallocate(new_length);
strcpy(data + length - 1, s.data); //overwrite null from s
length = new_length;
}
else {//if there was already enough space
strcpy(data + length - 1, s.data);
}
return *this;
}
Str &operator=(Str rhs) {
swap(*this, rhs);
return *this;
}
char &operator[](size_type i) { return data[i]; };
const char &operator[](size_type i) const { return data[i]; };
size_type size() { return length; }
const size_type size() const { return length; }
const char *c_str() const {
return data;
}
void copy(char *dest, size_type n) {
if (n > length)
throw std::out_of_range("Out of range");
std::copy(data, data + n, dest);
}
char *begin() { return data; };
char *end() { return data + length; };
void push_back(char c) {
if (length == capacity) {
reallocate(capacity == 0 ? DEFAULT_CAPACITY : 2 * capacity);
}
data[length++] = c;
}
private:
char *data;
std::allocator<char> alloc;
size_type length;
size_type capacity;
static const size_type DEFAULT_CAPACITY = 20;
void create(size_type n, char character_to_fill) {
capacity = length = n + 1;
data = alloc.allocate(capacity);
std::uninitialized_fill(data, data + length - 1, character_to_fill);
//alloc.construct(data + length - 1, '\0'); //is it needed to be constructed?
data[length - 1] = '\0';
}
void create(const char *s) {
capacity = length = strlen(s) + 1;
data = alloc.allocate(capacity);
strcpy(data, s);
//alloc.construct(data + length - 1, '\0');
data[length - 1] = '\0';
}
template<class In>
void create(In b, In e) {
capacity = e - b + 1;
data = alloc.allocate(capacity);
while (b != e) {
data[length++] = *(b++);
}
//alloc.construct(data + length -1, '\0');
data[length++] = '\0';
}
void reallocate(size_t new_capacity) {
char *new_data = alloc.allocate(new_capacity);
std::copy(data, data + length, new_data);
alloc.deallocate(data, length);
data = new_data;
capacity = new_capacity;
}
};
std::istream &operator>>(std::istream &is, Str &s) {
std::vector<char> buf;
char actual_character;
while (is.get(actual_character) && isspace(actual_character)) { ;
}
if (is) { //is it correct to check "is" ?
do buf.push_back(actual_character);
while (is.get(actual_character) && !isspace(actual_character));
if (is)
is.unget();
}
s.create(buf.begin(), buf.end());
return is;
}
std::ostream &operator<<(std::ostream &os, const Str &s) {
os << s.c_str();
return os;
}
Str operator+(Str lhs, const Str &rhs) {
lhs += rhs;
return lhs;
}
And example main.cpp
#include <iostream>
#include <vector>
#include "Str.h"
using std::cout;
using std::endl;
int main() {
Str s("Siema");
cout<<s.c_str()<<endl;
s = "Hello";
cout<<s<<endl;
s.push_back('a');
cout<<s<<endl;
Str t = "World";
//cout<<s+t<<endl; //THIS DOESNT WORK
s+=t;
cout<<s<<endl;
cout<<s[3]<<s[5]<<s[11]<<endl;
cout<<s.size()<<endl;
cout<<Str(s.begin()+3, s.end()-2)<<endl;
for(Str::iterator i = s.begin(); i<s.end() ; i+=2){
cout<<i<<endl;
}
char copied[3];
t.copy(copied, 4);
cout<<copied<<endl;
return 0;
}
In your code
char copied[3];
t.copy(copied, 4);
cout<<copied<<endl;
"copied" has only length of 3 while you are trying to copy 4 characters into it. Which will cause problem
Check updated codes below. Read comments with "<--"
str.h
#include <iostream>
#include <memory>
#include <vector>
class Str {
friend std::istream &operator >> (std::istream &, Str &);
void swap(Str &s, Str &t) {
std::swap(s.data, t.data);
std::swap(s.length, t.length);
std::swap(s.alloc, t.alloc);
}
public:
typedef char *iterator;
typedef size_t size_type;
Str() : data(nullptr), length(0), capacity(0) { }
Str(size_type length, char char_to_fill) : Str() { create(length, char_to_fill); }
Str(const char *s) : Str() { create(s); }
template<class In>
Str(In b, In e) : Str() { create(b, e); }
~Str() {
if (data) alloc.deallocate(data, capacity);
data = nullptr;
}
Str(const Str &s) {
*this = s;
}
// move constructor?
Str(Str &&other)
: Str() {// initialize via default constructor, C++11 only
swap(*this, other);
}
Str &operator+=(const Str &s) {
size_type new_length = length + s.length - 1; //remove 1 because of 2 nulls
if (new_length > capacity) {
reallocate(new_length);
strcpy(data + length - 1, s.data); //overwrite null from s
//length = new_length; //<-- You need to update the length anyay. Move it to before return
}
else {//if there was already enough space
strcpy(data + length - 1, s.data);
}
length = new_length; //<-- update the length
return *this;
}
Str &operator=(Str rhs) {
swap(*this, rhs);
return *this;
}
char &operator[](size_type i) { return data[i]; };
const char &operator[](size_type i) const { return data[i]; };
size_type size() { return length; }
const size_type size() const { return length; }
const char *c_str() const {
return data;
}
void copy(char *dest, size_type n) {
if (n > length)
throw std::out_of_range("Out of range");
std::copy(data, data + n, dest); // <--forgot about '\0'?
dest[n] = '\0'; // <-- add '\0'
}
char *begin() { return data; };
char *end() { return data + length; };
void push_back(char c) {
if (length == capacity) {
reallocate(capacity == 0 ? DEFAULT_CAPACITY : 2 * capacity);
}
data[length++ - 1] = c; //<-- length - 1 is the last position, because length here includes '\0'
data[length - 1] = 0; //<-- don't forget to add '\0'. It's better if you fill the unused spaces to '\0' after allocate them.
}
private:
char *data;
std::allocator<char> alloc;
size_type length;
size_type capacity;
static const size_type DEFAULT_CAPACITY = 20;
void create(size_type n, char character_to_fill) {
capacity = length = n + 1;
data = alloc.allocate(capacity);
std::uninitialized_fill(data, data + length - 1, character_to_fill);
//alloc.construct(data + length - 1, '\0'); //is it needed to be constructed?
data[length - 1] = '\0';
}
void create(const char *s) {
capacity = length = strlen(s) + 1;
data = alloc.allocate(capacity);
strcpy(data, s);
//alloc.construct(data + length - 1, '\0');
data[length - 1] = '\0';
}
template<class In>
void create(In b, In e) {
capacity = e - b + 1;
data = alloc.allocate(capacity);
while (b != e) {
data[length++] = *(b++);
}
//alloc.construct(data + length -1, '\0');
data[length++] = '\0';
}
void reallocate(size_t new_capacity) {
char *new_data = alloc.allocate(new_capacity);
std::copy(data, data + length, new_data);
alloc.deallocate(data, length);
data = new_data;
capacity = new_capacity;
}
};
std::istream &operator >> (std::istream &is, Str &s) {
std::vector<char> buf;
char actual_character;
while (is.get(actual_character) && isspace(actual_character)) {
;
}
if (is) { //is it correct to check "is" ?
do buf.push_back(actual_character);
while (is.get(actual_character) && !isspace(actual_character));
if (is)
is.unget();
}
s.create(buf.begin(), buf.end());
return is;
}
std::ostream &operator<<(std::ostream &os, const Str &s) {
os << s.c_str();
return os;
}
Str operator+(Str lhs, const Str &rhs) {
lhs += rhs;
return lhs;
}
and main:
int main() {
Str s("Siema");
cout << s.c_str() << endl;
s = "Hello";
cout << s << endl;
s.push_back('a');
cout << s << endl;
Str t = "World";
//cout<<s+t<<endl; //THIS DOESNT WORK
s += t;
cout << s << endl;
cout << s[3] << s[5] << s[11] << endl;
cout << s.size() << endl;
cout << Str(s.begin() + 3, s.end() - 2) << endl;
for (Str::iterator i = s.begin(); i<s.end(); i += 2) {
cout << i << endl;
}
char copied[5]; //<-- was 3, not enough space
t.copy(copied, 4);
cout << copied << endl;
return 0;
}
I have tried to write my own String class in C++ using Microsoft Visual Studio 2015. I wrote the class like this;
#include<string.h>
class myString {
private:
char* content;
public:
int size;
myString();
myString(char*);
~myString();
bool operator== (const myString &) const;
bool operator!= (const myString &) const;
myString operator= (const myString &);
myString operator+ (const myString &) const;
myString operator+= (const myString &);
friend std::ostream& operator<< (std::ostream &os, const myString &);
char operator[] (int &) const;
};
std::ostream& operator<<(std::ostream &os, const myString &string) {
os << string.content;
return os;
}
myString::myString() {
size = 0;
content = "\0";
}
myString::myString(char* newContent) {
size = strlen(newContent);
content = new char[size+1];
strcpy(content, newContent);
}
myString::~myString() {
delete[] content;
}
myString myString::operator= (const myString &string) {
if (size != string.size) {
delete[] content;
size = string.size;
content = new char[size+1];
}
strcpy(content, string.content);
return *this;
}
bool myString::operator== (const myString &string) const {
if (size != string.size)
return false;
if (strcmp(content, string.content))
return false;
return true;
}
bool myString::operator!= (const myString &string) const {
if (*this == string)
return false;
return true;
}
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
myString myString::operator+= (const myString &string) {
*this = *this + string;
return *this;
}
char myString::operator[] (int &index) const {
return content[index];
}
It works fine when I tried to do this;
#include<iostream>
#include "MyString.h"
using namespace std;
int main() {
myString s("my new");
cout << s+" string" << endl;
}
But I am not sure if there is any memory leak in operator+ function in the line char* newContent = new char[newSize]; I am allocating new space from the memory and I need it in the return statement return myString(newContent);.
So I can not deallocate it before this line and I can not deallocate it after the return statement. Am I correct, is there a memory leak? If so, how can I fix this?
EDIT 1 :
I've changed the operator+ function as follows with the help of Prince Dhaliwal;
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
But since I created the temp locally it calls its destructor before returning it and gives error. I supposed I should allocate memory for temp too. And I changed the function as follows;
myString myString::operator+ (const myString &string) const {
myString* temp= new myString;
int newSize = size + string.size;
char* newContent = new char[newSize+1];
temp->size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp->content = newContent;
return *temp;
}
It works fine now, but I believe there is still memory leak because of the temp variable. If there is a memory leak, how to fix this?
EDIT 2 :
I've fixed it simply by creating a Copy Constructor
There is actually a memory leak in your code. When you are using the + operator in s + " string". In your operator+() definition i.e.
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
You are allocating the new string here char* newContent = new char[newSize];, copying the older and new part to the new string. And again you are allocating the new string in the constructor return myString(newContent);. But where are you deleting your old string? Its nowhere in your code. So you have to delete the string newContent.
You can do this
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
UPDATE
You have to create a copy constructor.
myString(const myString &rhs) :
size(rhs.size) {
content = new char[size + 1];
strcpy(content, rhs.content);
}
I am trying to implement a basic string class in C++. However, I am stuck on the append() function. It would like it to be Hello World, but it results as Hello ÍWorlýýýý««««««««þ:
#define __START namespace lib{
#define __END }
__START
class string
{
public:
string(const char* s)
{
_str = s;
}
const char operator[](int position)
{
return _str[position];
}
void operator=(string s)
{
_str = s.c_str();
}
void append(string s)
{
size_t nlength = (length() + s.length());
char* nstr = new char[nlength];
for (int i = 0; i < length(); ++i)
{
nstr[i] = _str[i];
}
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
_str = const_cast<const char*>(nstr);
}
void operator+=(string s)
{
append(s);
}
const char* c_str()
{
return _str;
}
size_t length()
{
return strlen(_str);
}
std::string str()
{
return _str;
}
private:
const char* _str;
};
__END
int main()
{
lib::string s = "Hello ";
s.append("World"); // s += "World";
std::cout << s.c_str();
getchar();
}
There are a lot of errors, not only with append
string(const char* s)
{
_str = s;
}
The constructor is wrong, you should make a copy of s in order to free it later, this way:
~string()
{
delete[] _str; // or free(_str) in case you use malloc/realloc, thanks Fred!
}
Private member variable:
private:
const char* _str;
The internal string should not be const, you should be able to resize it later
const char operator[](int position)
{
return _str[position];
}
You are missing a check: length() > position
void operator=(string s)
{
_str = s.c_str();
}
You are not modifying s, it should be const string& s
You are also not copying s.c_str() which means that now s and this are sharing the same internal buffer
void append(string s) // s should be a const reference too
{
size_t nlength = (length() + s.length());
char* nstr = new char[nlength];
for (int i = 0; i < length(); ++i)
{
nstr[i] = _str[i];
}
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
_str = const_cast<const char*>(nstr);
}
Should be easier to write in terms of realloc:
void append(string s)
{
int start = length();
_str = realloc(_str, length() + s.length());
for (int i = 0; i < s.length(); i++) {
_str[start+i] = s[i];
}
}
If you want to stick to new its OK, but you must free _str before assigning it to the new one.
The following operators should be const:
const char* c_str() const;
size_t length() const;
std::string str();
Update: Options for the constructor:
// option one (use delete[] to cleanup _str)
string(const char* s) {
int n = strlen(s);
_str = new char[n+1];
memcpy(_str, s, n+1); // s is NULL terminated
}
// option two (use free() to cleanup _str)
string(const char* s) {
int n = strlen(s);
_str = (char*)malloc(n+1);
memcpy(_str, s, n+1); // s is NULL terminated
}
// option 3: rely on append taking a char* argument
string(const char *s) : _str(NULL) {
append(s, strlen(s));
}
..
void append(const string& s) {
append(s.c_str(), s.length())
}
void append(const char *s, int len) {
int start = _str ? length() : 0;
_str = realloc(_str, start + len);
for (int i = 0; i < len; i++) {
_str[start+i] = s[i];
}
}
Update 2: It will be better to use size_t or unsigned int instead of plain int because the size is always greater than or equal to zero.
There are tons of problems with your code, so let's do this step by step.
First, you don't need your preprocessor magic for the namespace, but just a basic namespace mynamespace{}.
Secondly, it is ideal to create a basic_string class, so that it can be used with different character types. char / wchar_t, e.t.c.
Problems with your class:
1) private: const char *_str;. The pointer will be modified, so the const is useless.
2) There are no typedefs. You need them if you are attempting to re-implement a STL class. (Explained in example)
3) Use an allocator. This way you can construct, and destroy elements, allocate, and deallocate memory. You will be certain to keep your memory a lot safer this way.
4) Strings must be null terminated. That means an extra '\0' at the end, meaning you will have to allocate an extra byte for that. Strings are null terminated, because it is a way of telling the code to stop reading the string.
5) You are assigning to a string which has not been allocated. _str = s.c_str(); could easily crash, depending on your compiler, because you are writing to non-allocated memory.
6) Use const references, rather than normal types for your parameters (string = const string &). You also need this for your copy constructor basic_string(const _Myt &).
I still may not have highlighted all of the problems
Example basic_string class
template < typename _Elem, typename _Traits = std::char_traits<_Elem>, typename _Alloc = std::allocator<_Elem> > class basic_string
{
public:
typedef basic_string<_Elem, _Traits, _Alloc> _Myt;
typedef _Elem value_type;
typedef _Traits traits_type;
typedef _Alloc allocator_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type *iterator;
typedef const value_type *const_iterator;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
basic_string()
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
basic_string(const_pointer _Init)
{
int count = 0;
__data = _Alloc().allocate(_Traits::length(_Init) + 1);
for (const_iterator i = &_Init[0]; i != &_Init[_Traits::length(_Init)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Init)], '\0');
}
basic_string(const _Myt &_Init)
{
if (this != &_Init)
{
int count = 0;
__data = _Alloc().allocate(_Traits::length(_Init.__data) + 1);
for (const_iterator i = &_Init.__data[0]; i != &_Init.__data[_Traits::length(_Init.__data)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Init.__data)], '\0');
}
else
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
}
~basic_string()
{
if (__data)
{
size_type tmp = size();
for (iterator i = begin(); i != end(); ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(__data, tmp);
}
}
_Myt &assign(const_pointer _Rhs)
{
int count = 0;
reserve(_Traits::length(_Rhs) + 1);
for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Rhs)], '\0');
return *this;
}
_Myt &operator=(const_pointer _Rhs)
{
return assign(_Rhs);
}
_Myt &append(const_pointer _Rhs)
{
int count = size();
reserve(size() + _Traits::length(_Rhs) + 1);
for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[count], '\0');
return *this;
}
_Myt &operator+=(const_pointer _Rhs)
{
return append(_Rhs);
}
iterator begin()
{
return &__data[0];
}
iterator end()
{
return &__data[size()];
}
size_type size()
{
return _Traits::length(__data);
}
_Myt &swap(basic_string<_Elem> &_Rhs)
{
std::swap(__data, _Rhs.__data);
return *this;
}
void reserve(size_type _Size)
{
int count = 0;
if (_Size < size())
{
return;
}
pointer buf = _Alloc().allocate(_Size);
for (iterator i = begin(); i != end(); ++i, ++count)
{
_Alloc().construct(&buf[count], *i);
}
std::swap(__data, buf);
for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(buf, _Traits::length(buf));
}
operator const_pointer()
{
return __data;
}
operator pointer()
{
return __data;
}
template < typename _Traits1, typename _Alloc1 > friend std::basic_ostream<_Elem> &operator<<(std::basic_ostream<_Elem> &_Stream, basic_string<_Elem, _Traits1, _Alloc1> &_Str)
{
return _Stream << _Str.c_str();
}
const_pointer data() const
{
return __data;
}
const_pointer c_str() const
{
return __data;
}
private:
pointer __data;
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
Of course, there is still a bit missing, but I'm sure you can implement that with a little online help.
First of all, your most critical problem is in char* nstr = new char[nlength];.
You must change it to char* nstr = new char[nlength+1];.
Then, in function append, after the two for loops, set nstr[nlength] = 0;
Second, for better performance (as well as correct coding), you probably need to change string s to const string& s in the following functions:
void operator=(string s)
void append(string s)
void operator+=(string s)
You have an off-by-one error in the second loop; the second string needs to be copied to length(), immediately after the end of the first:
for (int i = length(); i < nlength; ++i)
{
nstr[i] = s[i - length()];
}
You'll also need to allocate one more byte to put a null terminator on the end.
Note that you don't need that scary-looking cast to add const to the pointer, since that's a perfectly safe thing to do. const_cast is only needed to remove qualifiers. You might also want to fix the memory leak and, for bonus points, cache the length so you don't have to read the entire string every time you want it.
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
You first copy chars up to length()-1 and then start back at length() + 1, so you skip a char and there could be anything in there after allocation(unless you do a memset beforehand).
Then you need to terminate your string with a null character(\0) so that the std::string that gets constructed on str() method return knows when to stop reading chars.