basic string class

I'm trying to implement a basic string class for educational purposes (I'm told the std::string is mainly complicated template code), I've made a start below but seem to have done something wrong as I have 2 issues, intermittently, the output will never print the "size" (number of characters) at the end, and after getchar executes, and the destructor is called, I get a heap fragmentation error.

I'm not sure what's wrong though as I'm allocating strlen + 1 for the \0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//jstring.h
#include <ostream>

class jstring {
private:
	char* begin;
	char* end;
	int size;
public:
	jstring(const char*);
	~jstring();
	int length() { return size; }
	void print();
	friend std::ostream& operator << (std::ostream& os, const jstring& js)
	{
		return os << js.begin;
	}
};

//jstring.cpp

#include "jstring.h"
#include <iostream>

jstring::jstring(const char* data)
{
	begin = new char[sizeof(strlen(data)+1)];
	memcpy(begin, data, (strlen(data) + 1));
	end = begin;
	size = 0;
	while (*end != '\0')
	{
		size++;
		end++;
	}
}

jstring::~jstring()
{
	delete[] begin;
}

void jstring::print()
{
	for (char* i = begin; i != end; i++)
	{
		std::cout << *i;
	}
}


//main.cpp

#include "jstring.h"
#include <iostream>

int main(int argc, char** argv)
{
	jstring test("Test String");
	test.print();
	std::cout << "\n\n\n" << test << " " << test.length();
	getchar();
	return 0;
}
begin = new char[sizeof(strlen(data)+1)];

strlen(data) + 1 is an object of type size_t. The size of a that is typically 8 bytes (4 on a 32 bit system).

So the line of code begin = new char[sizeof(strlen(data)+1)];
is effectively identical to begin = new char[8];

You're creating an array of size 8(or 4 on a 32 bit system), always. This is very bad. You should be creating an array based on the size of the input string; not based on the size of the data type known as size_t.

Basically, what is that sizeof doing there? It has no business being there.
Last edited on
Thanks Repeater, getting rid of sizeof has fixed it.

Can't believe I missed that
ICantC wrote:
I'm told the std::string is mainly complicated template code
Using std::string isn't all that complicated. There are reasons why the code itself is.

Learning the basics of string manipulation by creating a custom string class is never a bad thing. IMO the work required to get the custom string class working makes using the C++ library std::string less of a pain.

I started learning C++ by using std::string. No char arrays. Later, after multiple attempts to create a custom string class I appreciated all the work that went into making the C++ library work.
Yeah I'm still going to use std::string for everything, I just wanted to get a handle on how they work behind the scenes a bit. I added a couple more operator overloads (i'm sure the + is very non-elegant), exception handles and used std::copy instead of memcpy, but I'm going to leave it at that and maybe try to make my own iterators and vector next.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//jstring.hpp

#pragma once
#include <iostream>
#include <ostream>

class jstring {
private:
	char* begin;
	char* end;
	int size;
public:
	jstring() = default;
	jstring(const char*);
	~jstring();
	int length() { return size; }
	void print();
	friend std::ostream& operator << (std::ostream& os, const jstring& js)
	{
		return os << js.begin;
	}
	friend jstring* operator + (jstring& a, jstring& b)
	{
		jstring* temp = new jstring;
		try {
			temp->begin = new char[(strlen(a.begin) + strlen(b.begin)) + 1];
		}
		catch (const std::bad_alloc& e) {
			std::cout << "jstring allocation failed: " << e.what();
		}
		memcpy(temp->begin, a.begin, strlen(a.begin) + 1);
		temp->end = temp->begin;
		while (*temp->end != '\0')
		{
			temp->end++;
		}
		memcpy(temp->end, b.begin, strlen(b.begin) + 1);
		temp->end = temp->begin;
		while (*temp->end != '\0')
		{
			temp->end++;
			temp->size++;
		}
		return temp;
	}
	char operator [] (int index)
	{
		if (index > size)
		{
			std::cout << "\n\nindex greater than string length";
			return 'E';
		}
		return begin[index];
	}
};

//jstring.cpp

#define _SCL_SECURE_NO_WARNINGS //for std::copy

#include "jstring.h"
#include <iostream>

jstring::jstring(const char* data)
{
	try {
		begin = new char[strlen(data) + 1];
	}
	catch (const std::bad_alloc& e) {
		std::cout << "\n\njstring allocation failed: " << e.what();
	}
	std::copy(data, data + strlen(data)+1, begin);
	//memcpy(begin, data, (strlen(data) + 1));
	end = begin;
	size = 0;
	while (*end != '\0')
	{
		size++;
		end++;
	}
}

jstring::~jstring()
{
	delete[] begin;
}

void jstring::print()
{
	for (char* i = begin; i != end; i++)
	{
		std::cout << *i;
	}
}
ICantC wrote:
Yeah I'm still going to use std::string for everything, I just wanted to get a handle on how they work behind the scenes a bit.

Now that is an attitude and outlook I can heartily support and agree with. :)

Here's to learning! :D
Topic archived. No new replies allowed.