I use VScode, and it shows "trace/breakpoint trap" on line:delete [] str;
Here is my code:
#include <iostream>
#include <cstring>
using namespace std;
class String
{
private:
char *str;
public:
String() : str(new char[1]) { str[0] = 0; }
const char *c_str() { return str; }
String operator=(const char *s);
~String()
{
delete[] str;
}
};
String String::operator=(const char *s)
{
delete[] str;
str = new char[strlen(s) + 1];
strcpy_s(str, strlen(s) + 1, s);
return *this;
}
int main()
{
String s;
s = "abc";
cout << s.c_str() << endl;
return 0;
}
the code stops at the destructor :
delete [] str;
I wonder what is going on.
Related
I am trying to create a class String which can be assigned by operator=. But the compiler shows an error:
error: conversion from ‘const char [5]’ to non-scalar type ‘String’ requested
Can anyone help me to fix it?
#include <iostream>
using namespace std;
class String
{
private:
char string[];
public:
void operator=(const char str[])
{
for (int i = 0; ; i++) {
if (str[i] == '\0') {
string[i] = str[i];
break;
} else {
string[i] = str[i];
}
}
}
friend ostream &operator<<(ostream &output, const String& str)
{
output << str.string;
return output;
}
};
int main()
{
String str1 = "test";
cout << str1 << endl;
}
String str1 = "test"; does not use operator= at all. It is just syntax sugar for String str1("test");, which uses a conversion constructor that you have not defined yet, hence the compiler error. You need to add such a constructor.
Also, char string[]; is not a valid variable declaration for an array. You need to specify a size for the array, and then make sure the class never exceeds that size.
For example
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char string[256];
public:
String(const char *str = NULL) {
if (str) strncpy(string, str, sizeof(string)-1);
string[sizeof(string)-1] = '\0';
}
String& operator=(const String &str) {
if (this != &str) {
memcpy(string, str.string, sizeof(string));
}
return *this;
}
friend ostream& operator<<(ostream &output, const String& str) {
output << str.string;
return output;
}
};
int main() {
String str1 = "test";
cout << str1 << endl;
}
However, in this situation, using a dynamically allocated array makes more sense than using a fixed array. Just be sure to follow the Rule of 3 for proper memory management.
Try this instead:
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char *string;
int length;
int capacity;
public:
String(const char *str = NULL)
: string(NULL), length(0), capacity(0)
{
if ((str) && (*str != '\0')) {
length = capacity = strlen(str);
string = new char[length + 1];
memcpy(string, str, length + 1);
}
}
String(const String &str)
: string(NULL), length(0), capacity(0)
{
if (str.string) {
length = capacity = str.length;
string = new char[length + 1];
memcpy(string, str.string, length + 1);
}
}
~String() {
delete[] string;
}
String& operator=(const String &str) {
if (this != &str) {
int len = str.length;
if (capacity >= len) {
memcpy(string, str.string, len + 1);
}
else {
int cap = int(double(len) * 1.5);
char *temp = new char[cap + 1];
memcpy(temp, str.string, len + 1);
delete[] string;
string = temp;
capacity = cap;
}
length = len;
}
return *this;
}
friend ostream& operator<<(ostream &output, const String& str) {
if (str.string) {
output.write(str.string, str.length);
}
return output;
}
};
int main() {
String str1 = "test";
cout << str1 << endl;
}
You need to add a ctor to your class. You are using the assignment operator to try to construct your String object. Add this to your class.
String(const char str[]) {
for (int i = 0; ; i++) {
if (str[i] == '\0') {
string[i] = str[i];
break;
} else {
string[i] = str[i];
}
}
}
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.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I had one assignment from my prof., but after coding, I got over 40 errors but don't know why :(
My assignment:
" Build CHAR and STRING class (STRING is a string of CHAR) with these samples:
class CHAR
{
private:
char content;
/* Insert more codes here */
};
class STRING
{
private:
CHAR * content;
int length;
/* Insert more codes here */
};
and then test with this main function:
int main()
{
CHAR c1, c2('c');
STRING s1, s2("s2"), s3('a'), s4(c1);
s1.expand(c2).expand('a').expand(s2).expand("abc");//s1: "cas2abc"
s1.remove(c2).remove('d');//remove all character c2 in s1 -- s1: "as2ab"
s1.input();//insert new string from keyboard
cout<<s1.getContent();// export content of s1
cout<<c1.getContent();// export content of c1
return 0;
}
My code link here: https://bugs.vn/10670
Thank you.
Check compiler errors!
Your STRING class does not have methods: remove, input, getContent. You can not call methods which you have not defined.
This will compile and run.
// CHAR.h
#include <iostream>
// STRING.h
#include <iostream>
#include <string>
#include <string.h> //sg7
using namespace std;
class CHAR
{
private:
char content;
public:
CHAR();
CHAR(char);
char GetContent();
void SetContent(char);
};
class STRING
{
private:
CHAR * content;
int length;
public:
STRING();
STRING(const char*);
STRING(char);
STRING(const CHAR&);
STRING& expand(const CHAR&);
~STRING();
};
// CHAR.cpp
// #include "CHAR.h"
CHAR::CHAR()
{
this->content = '\0';
}
CHAR::CHAR(char)
{
this->content = 'c';
}
char CHAR::GetContent()
{
return content;
}
void CHAR::SetContent(char C)
{
content = C;
}
// STRING.cpp
//#include "STRING.h"
//#include "CHAR.h"
STRING::STRING()
{
this->content = NULL;
this->length = 0;
}
STRING::STRING(const char *c)
{
this->length = strlen(c);
this->content = new CHAR[this->length];
for (int i = 0; i < this->length; i++)
{
this->content[i] = CHAR(c[i]);
}
}
STRING::STRING(char c)
{
this->length = 1;
this->content = new CHAR[1];
this->content[0] = CHAR(c);
}
STRING::STRING(const CHAR &c)
{
this->length = 1;
this->content = new CHAR[1];
this->content[0] = c; //.SetContent(c.GetContent());
}
STRING& STRING::expand(const CHAR &c)
{
CHAR *new_content = new CHAR[this->length + 1];
for (int i = 0; i < this->length; i++)
{
new_content[i] = this->content[i];
}
new_content[this->length] = c;
if(this->content!=NULL)
{
delete[] this->content;
}
this->content = new_content;
this->length++;
return *this;
}
STRING::~STRING()
{
if (this->content != NULL)
delete[]this->content;
}
int main()
{
CHAR c1, c2('c');
STRING s1, s2("s2"), s3('a'), s4(c1);
s1.expand('a');
return 0;
}
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);
}
I decided to code all assignments from last semesters c++ class over summer in order to better prepare for c++ 3 but I don't understand how to pass through a String class or what steps are even needed in order to concatenate two strings and the display the result in the main cpp file.
In my Main.cpp:
#include <iostream>
using namespace std;
#include "String.h"
int main()
{
String Str1;
String Str2("this is a test");
String Str3(Str2);
String Str4("bruh");
int result;
cout << "Testing Display: " << endl;
Str2.Display();
cout << endl;
cout << "Testing displayLine: " << endl;
Str2.displayLine();
cout << endl;
result = Str2.Compare(Str3);
if (result < 0)
{
Str2.Display();
cout << " comes before " << endl;
Str3.Display();
cout << endl;
}
else
if (result > 0)
{
Str3.Display();
cout << " comes before " << endl;
Str2.Display();
}
else
{
Str3.Display();
cout << " is equal to " << endl;
Str2.Display();
}
cout << endl;
result = Str2.Compare("wxyz");
Str1.Copy(Str3);
cout << "Str1 contains " << Str1.length() <<" characters"<< endl;
cout << "Concatenation: ";
Str2.Concat(Str4);
cout << endl;
return 0;
}
In my String.cpp:
#include <iostream>
using namespace std;
#include <string.h>
#include "String.h"
#pragma warning(disable:4996)
String::String()
{
NumChars = 0;
MaxSlots = 0;
pChar = new char[NumChars+1];
pChar[0] = '\0';
}
String::String(const char Str[])
{
NumChars = strlen(Str);
pChar = new char[NumChars + 1];
strcpy(pChar, Str);
}
String::String(const String & Str)
{
NumChars = Str.NumChars;
pChar = new char[NumChars + 1];
strcpy(pChar, Str.pChar);
}
String::~String()
{
delete[] pChar;
}
int String::Compare(const String & Str) const
{
return strcmp(pChar, Str.pChar); //case sensitive
}
int String::Compare(const char Str[]) const
{
return strcmp(pChar, Str); //case sensitive
}
String& String::Copy(const String & Str)
{
if (this != &Str)
{
if (MaxSlots < Str.NumChars)
{
delete[]pChar;
MaxSlots = Str.NumChars;
pChar = new char[NumChars + 1];
}
else;
NumChars = Str.NumChars;
strcpy(pChar, Str.pChar);
}
else;
return *this;
}
String& String::Copy(const char Str[])
{
delete[] pChar;
NumChars = strlen(Str);
MaxSlots = NumChars;
pChar = new char[MaxSlots + 1];
return *this;
}
String& String::Concat(const String & Str)
{
pTemp = new char[NumChars+1];
strcpy(pTemp, pChar);
strcat(pTemp, Str.pChar);
delete[]pChar;
pChar = pTemp;
return *this;
}
String & String::Concat(const char Str[])
{
return *this;
/*
NumChars = strlen(Str);
MaxSlots = NumChars;
delete[] pChar;
MaxSlots = MaxSlots + NumChars;
NumChars = NumChars + strlen(Str);
pChar = new char[MaxSlots + 1]; */
}
void String::Display() const
{
cout << pChar;
}
void String::displayLine() const
{
cout << pChar;
}
In my String.h:
#ifndef STRING_H
#define STRING_H
class String
{
public:
String(); //default constructor
String(const char[]);
String(const String &); //copy constructor
~String();
int Compare(const String &) const;
int Compare(const char[])const;
String& Copy(const String&);
String& Copy(const char[]);
String& Concat(const String&);
String& Concat(const char[]);
void Display()const;
void displayLine() const;
int length() const;
private:
char * pChar;
char *pTemp;
int NumChars;
int MaxSlots;
};
inline int String::length() const
{
return NumChars;
};
#endif
You expect the length of the concatenated string to be the sum of the length of the two strings. Therefore:
String& String::Concat(const String & Str)
{
pTemp = new char[NumChars + Str.NumChars + 1];
strcpy(pTemp, pChar);
strcat(pTemp, Str.pChar);
delete[]pChar;
pChar = pTemp;
return *this;
}
You can optimize this further by not strcat()-ing but strcpy()-ing twice (with an offset added to pTemp second time), as you already know the string length.
I'm not sure to understand the use of your MaxSlots; it's the size of allocated pChar (minus 1)? If so, there are points in your code where you forget to set/update/use.
And I don't understand what do you mean with "how to pass through a String class", but regarding "what steps are even needed in order to concatenate two strings", you've forgotten to take in count the number of chars already present in the object.
First of all, I suggest to create e Reserve() method; something like [caution: code not tested]
String& String::Reserve (int n)
{
if ( n > MaxSlots )
{
MaxSlots = n;
pTemp = new char[MaxSlots+1];
strcpy(pTemp, pChar);
delete[]pChar;
pChar = pTemp;
}
}
Next, rewrite your Concat() methods in this way
String& String::Concat(const String & Str)
{
NumChars += Str.NumChars;
Reserve(NumChars);
strcat(pChar, Str.pChar);
return *this;
}
String & String::Concat(const char * Str)
{
if ( Str )
{
NumChars += strlen(Str);
Reserve(NumChars);
strcat(pChar, Str);
}
return *this;
}
p.s.: sorry for my bad English