c++ deep copy constructor [duplicate] - c++

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

Related

Trace/Breakpoint trap:delete []

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.

error: conversion from ‘const char [5]’ to non-scalar type ‘String’ requested

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

Implement a class want to simulate std::string but get stuck when push_back it into a vector

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.

Strange program crash

I'm trying to implement a String class for an excercise. Here is my all code: (you don't need to read it all) :
#include <iostream>
using namespace std;
// ******************************* String ************************
class String {
private:
char* char_arr;
int strlen(const char* str) {
int length;
for(length=0;*(str+length)!='\0';length++);
return length;
}
void strCopy(const char* str1, char* str2) {
int length = strlen(str1);
for(int i=0;i<length;i++)
str2[i] = str1[i];
}
public:
// --- Constructor ---
String(const char* str) {
int length = strlen(str);
char_arr = new char[strlen(str)+1];
char_arr[length] = '\0';
strCopy(str,char_arr);
}
// --- size Constructor ---
explicit String(int size) {
char_arr = new char[size+1];
char_arr[size+1] = '\0';
}
// --- Destructor ---
~String() {
delete char_arr;
}
// --- Copy Constructor ---
String(const String& rhs) {
char_arr = new char[strlen(rhs.char_arr)+1];
strCopy(rhs.char_arr,char_arr);
}
// --- copy-assignment Constructor
const String& operator=(const String& rhs) {
delete char_arr;
char_arr = new char[strlen(rhs.char_arr)+1];
strCopy(rhs.char_arr,char_arr);
}
// --- operator== ---
bool operator==(const String& rhs) {
int this_length = strlen(char_arr);
int rhs_length = strlen(rhs.char_arr);
if(this_length==rhs_length) {
bool return_value = true;
for(int i=0;i<this_length;i++) {
if(char_arr[i]!=rhs.char_arr[i]) {
return_value = false;
break;
}
}
return return_value;
}
return false;
}
// --- operator+ ---
String operator+(const String& rhs) {
int this_length = strlen(char_arr);
int rhs_length = strlen(rhs.char_arr);
String new_str(this_length+rhs_length);
strCopy(char_arr,new_str.char_arr);
for(int i=0;i<rhs_length;i++) {
new_str.char_arr[i+this_length] = rhs.char_arr[i];
}
new_str.char_arr[this_length+rhs_length] = '\0';
return new_str;
}
// --- print ---
void print() {
cout << char_arr;
}
};
// ~~~~~~~ main ~~~~~~~~~
int main() {
String s = "This is";
String s1 = " My Name";
String s2 = s+s1;
s1.print();
return 0;
}
The problem is in the operator+ overloading. If you look at the main function and change the values of s and s1, the program sometimes will crash and sometimes it won't. any ideas why it's happens?
Your strCopy does not copy the trailing zero byte. While there is nothing woring with it, it is somewhat counterintuitive.
Your copy constructor uses the above strCopy() and does not add zero byte too, leaving copied string unterminated. The same applies to assignment operator.
Your concatenation operator dos not allocate enough room for trailing zero byte (but appends it). The program crashes because zero byte is placed after the allocated memory block.
Not sure this is the only problem, but you need to return *this in your const String& operator=(const String& rhs). Do enable warnings in the compiler, and it should tell you "reaching end of function without return".

why do I need both constructor and assignment operator here?

My code doesn't compile when one of these is omitted. I thought only copy assignment operator is required here in main(). Where is constructor needed too?
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class AString{
public:
AString() { buf = 0; length = 0; }
AString( const char*);
void display() const {std::cout << buf << endl;}
~AString() {delete buf;}
AString & operator=(const AString &other)
{
if (&other == this) return *this;
length = other.length;
delete buf;
buf = new char[length+1];
strcpy(buf, other.buf);
return *this;
}
private:
int length;
char* buf;
};
AString::AString( const char *s )
{
length = strlen(s);
buf = new char[length + 1];
strcpy(buf,s);
}
int main(void)
{
AString first, second;
second = first = "Hello world"; // why construction here? OK, now I know : p
first.display();
second.display();
return 0;
}
is this because here
second = first = "Hello world";
first temporary is created by AString::AString( const char *s ) ?
second = first = "Hello world"; first create a temporay AString with "Hello world", then first is assigned to it.
so you need AString::AString( const char *s ), but it is not the copy constructor.