Initializing an array of objects with {" ", " "} - c++

I am trying to initialize an array of String (the class contains a default constructor and a constructor like this one: String(const char* helper). I also have overloaded operator for <<;
However when I try to do the following, nothing happens :
String suits[4] = {"Hearts", "Clubs", "Diamonds", "Spades"};
std::cout << suits[2];
But this one is okay :
const String suit = "Hearts";
std::cout <<suit;
0
it works the same without the const. I wanted to build myself the string class. About the string class :
class String
{
char* buffer;
int size;
public:
String(const char* helper)
{
size_t sizeH = strlen(helper);
buffer = new char[sizeH];
for (int i = 0; i < sizeH; ++i)
{
buffer[i] = helper[i];
}
buffer[sizeH] = 0;
size = sizeH;
}
String()
{
buffer = nullptr;
size = 0;
}
void sizeOfBuffer()
{
std::cout <<size<<"\n";
}
~String()
{
delete[] buffer;
}

Related

Concatenate 2 string using operator+= in class C++

I've seen some similar questions before asking, but I'm still stuck at the part of concatenating two strings using operator+=.
Currently, I can get separate strings correctly by constructor method. But when I compile code, the line str[length+i] = s[i]; in the method String& String::operator+= (const String& s) shows an error:
no match for ‘operator[]’ (operand types are ‘const String’ and ‘unsigned int’)
So I need your help to fix this bug.
Here's my code:
#include <iostream>
#include <cstring>
class String {
// Initialise char array
char* data;
unsigned length;
public:
// Constructor without arguments
String();
// Constructor with 1 arguments
String(char* s);
// Copy Constructor
String(const String& source);
// Move Constructor
String(String&& source);
// Destructor
~String() { delete[] data; }
/*!
* #brief String length.
* #return Value in String #c length.
*/
unsigned len ( ) const;
/*!
* #brief Append to String.
* #param[in] s A String object.
* #return A String reference to *this.
* #post String will equal the concatenation of itself with #a s.
*/
String& operator+= (const String& s);
};
// Constructor with no arguments
String::String()
: data{ nullptr }
{
data = new char[1];
data[0] = '\0';
}
// Constructor with one arguments
String::String(char* s)
{
if (s == nullptr) {
data = new char[1];
data[0] = '\0';
}
else {
data = new char[strlen(s) + 1];
// Copy character of s[]
// using strcpy
strcpy(data, s);
data[strlen(s)] = '\0';
std::cout << data << "\n";
}
}
// Copy Constructor
String::String(const String& source)
{
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
data[strlen(source.data)] = '\0';
}
// Move Constructor
String::String(String&& source)
{
data = source.data;
source.data = nullptr;
}
unsigned String::len ( ) const
{
return length;
}
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
for (unsigned j=0; j < length; j++)
str[j] = data[j];
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
delete data;
length = len;
data = str;
return *this;
}
int main()
{
// Constructor with no arguments
String a;
// Convert string literal to
// char array
char temp[] = "Hello world.";
// Constructor with one argument
std::cout << "s1: ";
String s1{ temp };
// Copy constructor
String s11{ a };
char temp1[] = "Goodbye!";
std::cout << "s2: ";
String s2{ temp1 };
String s3 = String s1 + String s2;
return 0;
}
Another way of writing main function:
int main()
{
String s1("Hello World.");
String s2("Goodbye!");
std::cout << "s1: " << s1 << std::endl;
std::cout << "s2: " << s2 << std::endl;
String s3 = s1 + s2;
std::cout << "s3: " << s3 << std::endl;
std::cout << "The last char of s3: " << s3[s3.size()-1] << std::endl;
return 0;
}
Expected result:
s1: Hello World.
s2: Goodbye!
s3: Hello World.Goodbye!
The last char of s3: !
How can I modify my code to get s3 and last char of s3 correctly?
In many of your constructors, you do not set length which leaves it with an indeterminate value - and reading such values makes the program have undefined behavior. So, first fix that:
#include <algorithm> // std::copy_n
// Constructor with no arguments
String::String() : data{new char[1]{'\0'}}, length{0} {}
// Constructor with one argument
String::String(const char* s) { // note: const char*
if (s == nullptr) {
data = new char[1]{'\0'};
length = 0;
} else {
length = std::strlen(s);
data = new char[length + 1];
std::copy_n(s, length + 1, data);
}
}
// Copy Constructor
String::String(const String& source) : data{new char[source.length + 1]},
length{source.length}
{
std::copy_n(source.data, length + 1, data);
}
// Move Constructor
String::String(String&& source) : String() {
std::swap(data, source.data);
std::swap(length, source.length);
}
In operator+= you are trying to use the subscript operator, String::operator[], but you haven't added such an operator so instead of s[i], use s.data[i]:
String& String::operator+=(const String& s) {
unsigned len = length + s.length;
char* str = new char[len + 1];
for (unsigned j = 0; j < length; j++) str[j] = data[j];
for (unsigned i = 0; i < s.length; i++) str[length + i] = s.data[i];
str[len] = '\0';
delete[] data; // note: delete[] - not delete
length = len;
data = str;
return *this;
}
If you want to be able to use the subscript operator on String objects, you would need to add a pair of member functions:
class String {
public:
char& operator[](size_t idx);
char operator[](size_t idx) const;
};
char& String::operator[](size_t idx) { return data[idx]; }
char String::operator[](size_t idx) const { return data[idx]; }
And for String s3 = s1 + s2; to work, you need a free operator+ overload:
String operator+(const String& lhs, const String& rhs) {
String rv(lhs);
rv += rhs;
return rv;
}
Also, to support printing a String like you try in your alternative main function, you need an operator<< overload. Example:
class String {
friend std::ostream& operator<<(std::ostream& os, const String& s) {
os.write(s.data, s.length);
return os;
}
};
Full demo
For starters neither constructor sets the data member length.
So the operator
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
//...
has undefined behavior.
Also provided that the data member length was initialized you need to write
char* str = new char[len + 1];
instead of
char* str = new char[len];
to reserve memory for the terminating zero character '\0' because you are using the standard C string function strcpy in the copy constructor
strcpy(data, source.data);
And the class does not have the subscript operator used in this for loo[
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
And you forgot to append the terminating zero character '\0'.
Pay attention to that there is no member function size in the class used in this expression
s3[s3.size()-1]
And this construction
String s3 = String s1 + String s2;
is invalid. At least you should write
String s3 = s1 + s2;
and correspondingly define the operator +.

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

C++ class objects copy constructor and operator=

I'm currently building a library In C++. I have met this problem few days ago and I'm unable to fix it. I have shorten the code so it can be seen easier.
Below is my code:
class String
{
private:
mutable char* v;
mutable int l = 0;
public:
String()
{
l++;
v = new char[1];
*v = '\0';
}
String(const char* value)
{
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
String(const String& value)
{
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
int len() const
{
return l - 1;
}
char* val() const
{
return v;
}
char* operator=(const char* value) const
{
delete[] v;
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char* operator=(const String& value) const
{
delete[] v;
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char operator[](const int& index) const
{
return v[index];
}
};
class StringArray
{
private:
union ArrayDef
{
public:
mutable String stringV;
mutable int intV;
ArrayDef()
{
}
ArrayDef(const String& value)
: stringV(value)
{
}
ArrayDef(const int& value)
: intV(value)
{
}
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
String operator=(const String& value) const
{
stringV = value;
return stringV;
}
int operator=(const int& value) const
{
intV = value;
return intV;
}
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
};
mutable ArrayDef* arrdef;
mutable int arrLen = 0;
public:
StringArray()
{
}
void add(const ArrayDef& value) const
{
ArrayDef temp[arrLen + 1];
for (int i = 0; i < arrLen; i++)
temp[i] = arrdef[i];
temp[arrLen] = value;
arrLen++;
delete[] arrdef;
arrdef = new ArrayDef[arrLen];
for (int i = 0; i < arrLen; i++)
arrdef[i] = temp[i];
}
int len() const
{
return arrLen;
}
ArrayDef val(const int& index) const
{
return arrdef[index];
}
};
And my driver code:
#include <iostream>
int main()
{
StringArray arr;
arr.add(String("Hello"));
arr.add(String("World"));
std::cout << "Length of the array: " << arr.len() << std::endl;
int indexOfString = 1;
int indexOfCharacter = 2;
char s = arr.val(indexOfString).stringV[indexOfCharacter];
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have created two class, that is, String and StringArray class.
For String class, I need to always add a null character after the char pointer array for safety issue.
For StringArray class, I uses union because it's actually an array for multiple types.
It can be successfully compiled but it output some random character and it is different every time I run it.
Any answers will be appreciated, and please tell me why and how it don't works. Thank you.
From,
HaiQin.
This code is just a collection of antipatters that makes it difficult to study. What is the reason of making the internal data mutable? Why do you need to play with length and l where sometimes it is the length of the string, sometimes it is the size of array? The operator operator= returns char* which is a bad practice. Using const int& index as a parameter is a strange choice. You allocate arrays multiple times but you have no destructor that frees the memory.
Here your assignment operator returns a value, not reference!
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
Next comes even more dangerous practice:
// Recollect that this is a union
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
You are assigning both fields of the union at the same time! Did you mean struct?
Try to fix that. Start with changing union to structure.
One of the things that certainly will not work is the ArrayDef copy constructor and operator=(const ArrayDef & value). This is because you may only use the active value in the union, not both at the same time. This is usually solved by using a tagged union. Is there a reason you cannot use the Standard Template Library?
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> arr;
arr.push_back(std::string("Hello"));
arr.push_back(std::string("World"));
std::cout << "Length of the array: " << arr.size() << std::endl;
constexpr int indexOfString = 1; // second string - starting from 0!
constexpr int indexOfCharacter = 2; // third character
char s = arr.at(indexOfString).c_str()[indexOfCharacter]; // using interfaces closest to the original
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}

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

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 *