I have wrote a useless class, containing move constructor, default constructor and other construct. But this code (useless four(one + two)) didn't call move constructor. By the way, I have another question. Is safe when I delete a null pointer?
class useless {
private:
int len;
char * p; // address of string
public:
~useless();// default deconstruct
explicit useless(int len);// len construct
useless(int len, char c);// two argument construct
useless(useless&& u); //move construct
useless operator+(const useless& u) const; // operator +
};
useless::useless(int l) {
len = l;
p = new char[len];
}
useless::useless(int l, char c) {
len = l;
p = new char[len];
for(int i =0;i<len;i++){
p[i] = c;
}
}
useless::useless(useless&& u) {//move construct
len = u.len;
p = u.p;
u.p = nullptr;
u.len = 0;
}
useless::~useless() {
delete[] p;
}
useless useless::operator+(const useless &u) const {
useless re = useless(n + u.n);
for(int i =0; i < n; i++){
re.p[i] = p[i];
}
for(int i = n;i < re.n; i++){
re.p[i] = u.p[i - n];
}
return re;
}
int main(){
useless one(2,'x');
useless two = one;
useless four(one + two);
}
Related
I have nearly finished creating my own custom string class. However, it seems not going well when the program did not return the output that I was expected. In detail:
Input:
string a = "Hello"
string b = "World!"
Expected output:
HelloWorld!
!dlroWolleH
Actual output:
Hello
Here is my code:
#ifndef _STRING
#define _STRING
#include<iostream>
#include<cstring>
class string {
private:
char* s = nullptr;
unsigned int size = 0;
public:
string();
~string() { delete s; };
string(char* );
string(const char* );
string(const string&);
friend std::ostream& operator << (std::ostream&, string&);
friend string operator +(string, string);
string& operator = (const string&);
string& operator = (const char&);
string& inverse();
char* inconst();
char* output() const{
return s;
}
};
#endif
string::string() :s{ nullptr } {
size = 1;
s = new char[size];
s[0] = '\0';
}
string::string(char* source) {
if (source == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(source) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source[k];
}
}
}
string::string(const char* source) {
if (source == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(source) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source[k];
}
}
}
string::string(const string& t) {
size = t.size;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = t.s[k];
}
}
string& string::operator=(const string& source) {
delete[] s;
size = source.size;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source.s[k];
}
return *this;
}
string& string::operator=(const char&source) {
const char* t = &source;
if (t == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(t) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = t[k];
}
}
return* this;
}
string operator +(string a, string b) {
string t;
t.size = a.size + b.size;
t.s = new char[t.size + 1];
strncpy_s(t.s, a.size + 1, a.s, a.size);
strncpy_s(t.s + a.size, b.size + 1, b.s, b.size);
return t;
}
std::ostream& operator << (std::ostream& os, string& source) {
os << source.output();
return os;
}
char* string::inconst() {
char* t;
t = new char[size + 1];
for (size_t k = 0; k < size; k++)
{
t[k] = s[size - 1 - k];
}
t[size] = '\0';
return t;
}
string& string::inverse() {
this->s = this->inconst();
return*this;
}
int main(){
string a = "Hello";
string b = "World!";
string c = a + b;
std::cout << c << std::endl;
std::cout << c.inverse() << std::endl;
system("pause");
return 0;
}
It seems like that I have caught some errors at concatenation part ( overloading operator + assignment) since I can receive the output i want when i output separated variable a or b like std::cout << b << std::endl; but I cannot find out exactly what I was wrong. Please help me fix my code and thanks for helping me
Your string does store the terminating null character at s[size - 1].
Then in inconst you take the null character not into account, but pretend that size is number of characters in the string.
char* string::inconst() {
char* t;
t = new char[size + 1]; // why +1 here ?
for (size_t k = 0; k < size; k++)
{
t[k] = s[size - 1 - k];
}
t[size] = '\0'; // why another \0 ?
return t;
}
The first iteration assigns s[size-1] to t[0], ie in the end the string stored in t looks like this:
t[0] t[1] ... t[size] t[size+1]
\0 s[size-2] ... s[0] \0
You need to decide if size does count the null terminator or not and then be consistent about that.
Presumably this won't be the last time you need to do debugging, hence I propose to instrument the operator<< to print more information and not rely on the char* overload. Write a loop that prints character by character. If I didnt miss anything and my answer is correct you will see the contents of your string as explained above.
There are more issues in your code, the one I spotted is operator= not handling self assignment correctly. When you do
string s;
s = s;
Then your operator= first deletes the buffer and then copies from it. Just in case you are not convinced that this is an issue (because who would write s=s, thats silly, no?), consider for example a function void foo(string& a, string b&) { a = b; }.
On a more stylistic note, I suggest you to rename the method. inverse suggests that it would return a new string, while invert would make it clear that it modifies the string. (And I have no clue what inconst is supposed to mean.)
I simulated a vector but the constructor doesn't work; when I call pop() function it assigns garbage value to my old object in vector class.
vector(vector &v) {
vec = new T[v.size()];
memcpy(vec, v,v.size());
size_arr = v.size();
}
here's entire code:
#include <iostream>
using namespace std;
template <typename T>
class vector {
int size_arr;
T * vec = new T;
public:
vector(/*int _size*/) {
vec = new T[0];
size_arr = 0;
};
~vector() {
size_arr = 0;
delete[] vec;
};
vector(vector &v) {
vec = new T[v.size()];
memcpy(vec, v,v.size());
size_arr = v.size();
}
void push_back(T data) {
T *temp = new T[size_arr + 1];
for (int i = 0; i < size_arr; i++)
temp[i] = vec[i];
temp[size_arr] = data;
size_arr++;
delete[] vec;
vec = temp;
};
void push_front(T data){
int j;
T *temp = new T[size_arr + 1];
for ( j = size_arr; j >= 0;j--) {
temp[j + 1] = vec[j];
}
temp[0] = data;
delete[] vec;
vec = temp;
size_arr++;
};
void insert(int index, T data) {
int j;
T *temp = new T[size_arr + 1];
for (int i = 0; i < size_arr ;i++)
temp[i] = vec[i];
for (int i = 0; i < size_arr;i++) {
if (i == index) {
for ( j = size_arr; j >=i;j--) {
temp[j+1] = vec[j];
}
temp[j + 1] = data;
delete[] vec;
vec = temp;
size_arr++;
}
}
};
void pop() {
T *temp = new T[size_arr - 1];
for (int i = 0; i < size_arr-1;i++)
temp[i] = vec[i];
size_arr--;
delete[] vec;
vec = temp;
};
void Delete(int index)
{
T *temp = new T[size_arr - 1];
for (int i = 0; i < index;i++)
temp[i] = vec[i];
for (int i = 0; i < size_arr;i++) {
if (i == index) {
for (int j = i; j < size_arr-1;j++) {
temp[j] = vec[j + 1];
}
size_arr--;
delete[] vec;
vec = temp;
}
}
};
int search(T data) {
for (int i = 0; i < size_arr;i++) {
if (vec[i] == data) {
return i;
}
}
return -1;
};
int size() { return size_arr; };
};
int main() {
vector <int>test;
test.push_front(2);
test.push_front(3);
test.push_back(0);
test.push_back(-1);
test.insert(2, 2);
test.pop();
vector <int > test1;
test1 = test;// problem
test1.pop();
}
The problem is the line test1 = test;// problem, which does not call the copy constructor, but the assignment operator. You did not declare this operator, so the compiler will use the default implementation, which simply copies all member. So, after the assignment test1.vec and test.vec point to the same memory location.
When you change the line (and the one above it) to vector <int > test1{test};, it will call your copy constructor.
You also forgot to #include <cstring> for memcpy, which you should not use for non-POD types.
You have to multiply the size in memcpy with sizeof(T), because memcpy works on bytes, not on types. You also have to use v.vec instead of v.
Here is the fixed version: https://ideone.com/JMn7ww
I think the problem is in your copy operator. You use memcpy() which is a c function. Which should in itself not be a problem (apart from it being not so nice in many opinions). But since memcpy() is a c function, it doesn't know about types, and it takes its size arguments as a count of bytes.
the element you put in is int which is probably 4 bytes. So when your copy contstructor gets called, and the original has 3 elements, there will be 12 bytes in your array, but malloc will only copy 3 of them.
The comments of other people about not properly copying template types are right, so if you make a vector of strings, you cannot just memcpy them, and assume the result will be new strings. For this answer i was assuming you using only basic types as your template arguments like int, or double.
So I was trying to implement String class and I got an error saying segmentation fault. I think there is memory leak in my constructor. Can you please tell me what I am doing wrong? Thank you.
here is the constructor code and I am not permitted to use any standard library functionality.
String(const char* chars){
int i = 0;
if(chars){
while(chars[i]){
i++;
}
}
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
};
And also this is my full code:
#include <iostream>
using namespace std;
class String{
public:
char* str;
int len;
String(){
str = new char[0];
len = 0;
};
String(const char* chars){
int i = 0;
if(chars){
while(chars[i]){
i++;
}
}
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
};
String(const String& s){
if(s.isEmpty() == false){
len = s.len;
str = new char[len];
for(int i = 0; i < len; i++){
str[i] = s.str[i];
}
}
};
~String() noexcept{
if(len > 0)
delete[] str;
};
bool isEmpty() const noexcept{
if(len == 0){
return true;
}
else{
return false;
}
}
unsigned int length() const noexcept{
return len;
}
const char* toChars() const noexcept{
char* temp = new char[len];
int c = 0;
while(temp[c] != '\0'){
temp[c] = str[c];
c++;
}
return temp;
}
};
int main()
{
const char* chars = "Boo is snoring";
String s;
String t{chars};
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
}
The problem with your String(const char* chars) constructor is on this statement:
str = new char[len - 1];
You are allocating less memory then you need, so your for loop is going out of bounds of the allocated memory. You need to allocate at least len number of chars, not len - 1 number of chars:
str = new char[len];
If you intend str to be null-terminated, you need to allocate len + 1 number of chars instead, and then insert a null terminator after the loop is finished:
str = new char[len + 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
str[len] = '\0';
You are making a similar mistake in your toChars() method. You are not null-terminating the output, which is required by the operator<< that you are passing the memory to afterwards:
const char* toChars() const {
char* temp = new char[len + 1];
for(int c = 0; c < len; ++c){
temp[c] = str[c];
}
temp[len] = '\0';
return temp;
}
Note that I removed the noexcept on toChars(). This is because new[] is not noexcept, it can throw a std::bad_alloc exception if it can't allocate memory, which you are not catching. noexcept means the method doesn't throw any exception at all, but that is not the case here.
There are other problems with your code, too:
Your destructor leaks memory if len is 0, ie if your Default constructor is called, or your Converting constructor is called with a null/empty string. If you call new[], you need to call delete[], regardless of the len used.
Your Copy constructor is not initializing str or len if the String being copied is empty.
In main(), you are not delete[]'ing the memory that toChars() returns.
optional for this particular situation, but in general, you are missing a Copy Assignment operator. And, since you are clearly using C++11 or later, you should also add a Move constructor and a Move Assignment operator as well. See the Rule of 3/5/0.
Try this instead (with no additional library functions added, per request):
#include <iostream>
using namespace std;
class String {
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars) {
if (chars) {
unsigned int i = 0;
while (chars[i]) {
++i;
}
len = i;
str = new char[len];
for(int j = 0; j < len; ++j) {
str[j] = chars[j];
}
}
}
String(const String& s) {
if (!s.isEmpty()) {
len = s.len;
str = new char[len];
for(int i = 0; i < len; ++i){
str[i] = s.str[i];
}
}
}
~String() noexcept {
delete[] str;
}
String& operator=(const String &s) {
if (&s != this) {
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
}
return *this;
}
bool isEmpty() const noexcept {
return (len == 0);
}
unsigned int length() const noexcept {
return len;
}
const char* toChars() const {
char* temp = new char[len + 1];
for(unsigned int c = 0; c < len; ++c) {
temp[c] = str[c];
}
temp[len] = '\0';
return temp;
}
};
int main()
{
String t{"Boo is snoring"};
const char *chars = t.toChars();
cout << "t.len : " << t.length() << endl << "toChar() : " << chars << endl;
delete[] chars;
return 0;
}
Although, a simpler way to implement toChars() would look like this instead:
#include <iostream>
using namespace std;
class String {
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars) {
if (chars) {
unsigned int i = 0;
while (chars[i]) {
++i;
}
len = i;
str = new char[len + 1];
for(int j = 0; j < len; ++j) {
str[j] = chars[j];
}
str[len] = '\0';
}
}
String(const String& s) {
if (!s.isEmpty()) {
len = s.len;
str = new char[len + 1];
for(int i = 0; i < len; ++i){
str[i] = s.str[i];
}
str[len] = '\0';
}
}
~String() noexcept {
delete[] str;
}
String& operator=(const String &s) {
if (&s != this) {
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
}
return *this;
}
bool isEmpty() const noexcept {
return (len == 0);
}
unsigned int length() const noexcept {
return len;
}
const char* toChars() const noexcept {
return str ? str : "";
}
};
int main()
{
String t{"Boo is snoring"};
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
}
You get crash cause you allocate len - 1 chars new char[len - 1], but copy len chars for(int j = 0; j < len; j++) and do not copy zero-char in the end.
The first constructor should be
String(const char* chars) {
len = 0;
if (chars) {
while (chars[len])
len++;
}
str = new char[len + 1];
for (int j = 0; j < len; j++){
str[j] = chars[j];
}
str[len] = 0;
};
I hope you are able to update the second constructor properly.
The destructor should be
~String() noexcept{
delete[] str;
}
I hope you are able to fix other issues.
When I run my code i get a segmentation fault for calling the default constructor, but not every time I call it. It faults when I try to dynamically allocate new memory after overloading the + operator. I know seg faults happen with misuse of pointers but I call the def. constructor prior in the code and it works.
here is a git repo with main and header if needed:
#include <iostream>
#include "proj1-5-MyString.h"
#include <cmath>
using namespace std;
MyString::MyString() {
capacity = 10;
size = 0;
data = new char[capacity];
data[size] = '\0';
}
//Constructor
MyString::MyString(const char *input) {
this->capacity = 10;
this->size = 0;
this->data = new char[this->capacity];
for (int count = 0; input[count] != '\0'; count++) {
if (count >= this->capacity) {
this->capacity *= 2;
char *temp = new char[this->capacity];
for (int i = 0; i < this->size; i++) {
temp[i] = this->data[i];
}
delete[] this->data;
this->data = temp;
delete[] temp;
}
this->data[count] = input[count];
this->size = count + 1;
}
while ((double) this->size < 0.25 * ((double) (this->capacity))) {
this->capacity = (int)floor(this->capacity / 2);
}
this->data[this->size + 1] = '\0';
}
//Constructor with an initialization character string
MyString::~MyString() {
delete[] this->data;
data = NULL;
}
//Destructor
MyString::MyString(const MyString &that) {
this->data = new char[this->capacity];
this->capacity = that.capacity;
this->size = that.size;
for(int i = 0; i < size; i++){
this->data[i] = that.data[i];
}
while(this->size < 0.25 * (double)(this->capacity)){
this->capacity = (int)((double)that.capacity / 2.0);
}
}
//Copy constructor
MyString &MyString::operator=(const MyString &input) {
if (this->data != input.data) {
delete[] this->data;
this->capacity = input.capacity;
this->size = input.size;
int charCount = 0;
for ( charCount; charCount < input.size; charCount++) {
if (charCount >= this->capacity) {
this->capacity *= 2;
char *temp = new char[this->capacity];
for (int j = 0; j < charCount; j++) {
temp[j] = this->data[j];
}
delete[] this->data;
this->data = temp;
delete[] temp;
}
this->data[charCount] = input.data[charCount];
this->size = charCount + 1;
}
}
return *this;
}
//Overloaded assignment operator, make a copy of MyString object
bool MyString::operator==(const MyString &otherString) const {
int i = 0;
bool same = true;
if (this->size == otherString.size) {
while (i < otherString.size) {
if (this->data[i] == otherString.data[i]) {
same = i <= size;
} else {
same = false;
}
i++;
}
} else {
same = false;
}
return same;
}
//overloaded equivalence relational operator
char &MyString::operator[](int val) {
return this->data[val];
}
//overloaded [ ] should return a char by reference
void MyString::operator+=(const MyString &otherStr) {
*this = *this + otherStr;
}
//overloaded += operator, use to concatenate two MyStrings
MyString MyString::operator+(const MyString &otherStr) const {
//SEGFAULT HERE
MyString doubleString;
cout << doubleString.capacity << endl;
while (doubleString.capacity < (this->size + otherStr.size)) {
doubleString.capacity *= 2;
}
for (int i = 0; i < max(this->size, otherStr.size); i++) {
doubleString.data[i] = this->data[i];
doubleString.data[i + this->size] = otherStr.data[i];
}
doubleString.size = this->size + otherStr.size;
return doubleString;
}
//Create a new MyString object that is the concatenation of two MyString
//objects
void MyString::getline(istream &in, char delimit) {
delimit = '\n';
int size = 10;
char buffer[size];
int charNum = 0;
while (in.get(buffer[charNum++])) {
if (buffer[charNum - 1] == delimit) {
break;
}
if (charNum >= size) {
char *temp = new char[size * 2];
for (int j = 0; j < size; j++) {
temp[j] = data[j];
}
delete[] data;
data = temp;
delete[] temp;
}
}
}
//reads an entire line from a istream. Lines are terminated with delimit
//which is newline ‘\n’ by default
int MyString::length() const {
return this->size;
}
//return the length of the string
ostream &operator<<(ostream &out, MyString &stringWord) {
for (int i = 0; i < stringWord.size; i++) {
out << stringWord.data[i];
}
return out;
}
//overloaded insertion operator
For instance, if you have this code:
MyString s0 = "ABC";
MyString s1 = "DEF";
auto s2 = s0 + s1;
The first problem is in the constructor of MyString. Your strings are not \0 terminated.
This can be fixed quite easiely though:
MyString::MyString(const char *input)
{
this->capacity = 10;
this->size = 0;
//this->data = new char[this->capacity]; //<- Old
this->data = new char[this->capacity](); //<- New
There are multiple places where you don't initialize the allocated char array. Please check your code and do so. Either via the char() constructor or various other ways (std::fill, memset, ...).
Please remember to debug your own code before posting a question. You can spot this in the debugger pretty easiely.
I have made the next class for a 3d array. Declaring a variable as, for example
Grid3d n = Grid3d(2,2,2);
n(0,0,0) = 1;
works fine, but declaring it as
Grid3d n;
n = Grid3d(2,2,2);
n(0,0,0) = 1;
Gives me a segmentation fault, the problem seems to be the default constructor, but i don't know how to fix it, any clue?
#ifndef _GRID3D_
#define _GRID3D_
#include <iostream>
#include <cmath>
#include <cassert> // assert()
using namespace std;
class Grid3d
{
private:
int L;
int M;
int N;
double *** G;
public:
Grid3d(int,int,int);
Grid3d();
Grid3d(const Grid3d &);
~Grid3d();
double & operator()(int,int,int);
};
#endif
//Constructor
Grid3d::Grid3d(int L,int M,int N)
:L(L), M(M), N(N)
{
int i,j,k;
G = new double ** [L];
for (i=0;i<L;i++){
G[i] = new double * [M];
for (j=0;j<M;j++){
G[i][j] = new double [N];
for (k=0;k<N;k++){
G[i][j][k] = 0;
}
}
}
}
//Constructor vacío
Grid3d::Grid3d()
:L(0), M(0), N(0)
{
G = NULL;
}
//Constructor copia
Grid3d::Grid3d(const Grid3d &A)
:L(A.L), M(A.M), N(A.N)
{
G = new double ** [L];
int i,j,k;
for (i=0;i<L;i++){
G[i] = new double * [M];
for (j=0;j<M;i++){
G[i][j] = new double [N];
for (k=0;k<N;k++){
G[i][j][k] = A.G[i][j][k];
}
}
}
}
//Destructor
Grid3d::~Grid3d()
{
// Libera memoria
for (int i=0;i<L;i++){
for (int j=0;j<M;j++){
delete [] G[i][j];
G[i][j] = NULL;
}
delete [] G[i];
G[i] = NULL;
}
delete G;
G = NULL;
}
double& Grid3d::operator()(int i,int j,int k)
{
assert(i >= 0 && i < L);
assert(j >= 0 && j < M);
assert(k >= 0 && k < N);
return G[i][j][k];
}
Assignment operator
Grid3d Grid3d::operator = (const Grid3d &A)
{
if (this == &A) {return *this;};
if (G != NULL){
// Libera memoria
for (int i=0;i<L;i++){
for (int j=0;j<M;j++){
delete [] G[i][j];
G[i][j] = NULL;
}
delete [] G[i];
G[i] = NULL;
}
delete G;
G = NULL;
}
L = A.L;
M = A.M;
N = A.N;
G = new double ** [L];
int i,j,k;
for (i=0;i<L;i++){
G[i] = new double * [M];
for (j=0;j<M;i++){
G[i][j] = new double [N];
for (k=0;k<N;k++){
G[i][j][k] = A.G[i][j][k];
}
}
}
return *this;
}
You have dynamically allocated memory, but you have not followed the rule of three. You are missing an assignment operator, so when you do this:
Grid3d n;
n = Grid3d(2,2,2); // both RHS temporary and n point to the same data.
n(0,0,0) = 1; // Accessing deleted memory: undefined behaviour.
you will have two attempted de-allocations of the same memory, because you have n's pointer pointing to the same memory as the temporary used to assign to it in the second line. When the temporary dies, it de-allocated it's memory. When n dies, it tries to de-allocate the same memory. Furthermore, any access to that memory after the second line is undefined behaviour.
It's a trivial typo. I'll point out the line, and I'll let you spot where it's gone wrong:
for (j=0;j<M;i++)
That is, aside from the missing assignment operator, which makes the original constructed object in n = Grid3d(2,2,2); be freed, and thus you are accessing G[i][j][k] in operator() with NULL pointers.