This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I have a question about user defined conversion.
class String {
char* m_data;
public:
String(): m_data(NULL) {}
String(const char* cstr): m_data(new char[strlen(cstr)+1]) {
strcpy(m_data, cstr);
}
~String() {
delete[] m_data;
}
String& operator=(const char* cstr) {
delete[] m_data;
m_data = new char[strlen(cstr)+1];
strcpy(m_data, cstr);
return *this;
}
operator const char*() const {
return m_data;
}
};
While this works:
int main(int argc, char** argv) {
String a;
String b;
a = "aaa";
b = (const char *)a;
return 0;
}
This does not:
int main(int argc, char** argv) {
String a;
String b;
a = "aaa";
b = a;
return 0;
}
I get double free or corruption runtime error. Valgrind says something about invalid delete.
Why do I have to explicitly typecast it? I thought it would work this way with explicit operator const char*(). Am I doing something wrong?
You forgot to define copy assignment operator:
String& operator=(const String& other) {
if(this != &other) {
char* new_data = new char[strlen(other.m_data)+1];
strcpy(new_data, other.m_data);
delete[] m_data;
m_data = new_data;
}
return *this;
}
Because of that your compiler had to define "default" copy assignment operator, which simple assigns all fields of "other" to a current object:
String& operator=(const String& other) {
m_data = other.m_data;
return *this;
}
So you have two pointers to same m_data in a and b and on the exit from main, delete[] will be called twice.
Related
These are sources that work:
MyString.h
#pragma once
class CMyString {
public:
CMyString();
CMyString(const CMyString &rhs);
CMyString(const char* param);
~CMyString();
private:
char* m_pszData;
int m_nLength;
public:
int setString(const char* pszParam);
const char* getString() const;
void release();
CMyString& operator=(const CMyString &rhs);
operator char*() const;
};
MyString.cpp
#include "MyString.h"
#include <iostream>
#include <cstring>
CMyString::CMyString() : m_pszData(NULL), m_nLength(0) {
}
CMyString::CMyString (const CMyString &rhs) : m_pszData(NULL), m_nLength(0) {
this->setString(rhs.getString());
}
CMyString::CMyString (const char* pszParam) : m_pszData(NULL), m_nLength(0) {
setString(pszParam);
}
CMyString::~CMyString() {
release();
}
int CMyString::setString(const char* pszParam) {
release();
if(pszParam == NULL)
return 0;
m_nLength = strlen(pszParam);
if(m_nLength == 0)
return 0;
m_pszData = new char[m_nLength + 1];
strncpy(m_pszData, pszParam, m_nLength);
return m_nLength;
}
const char* CMyString::getString() const {
return m_pszData;
}
void CMyString::release() {
if(m_pszData != NULL)
delete[] m_pszData;
m_pszData = NULL;
m_nLength = 0;
}
CMyString &CMyString::operator = (const CMyString &rhs) {
if(this != &rhs)
this->setString(rhs.getString());
return *this;
}
CMyString::operator char*() const { return m_pszData; }
StringCtrlSample.cpp
#include "MyString.h"
#include <iostream>
using namespace std;
void testFunc(const CMyString &strParam) {
cout << strParam << endl;
}
int main(int argc, char *argv[]) {
CMyString strData("Hello");
cout << strData.getString() << endl;
::testFunc(strData);
::testFunc(CMyString("World"));
return 0;
}
I tried operator overloading about char*() without "const" keyword at MyString.h and MyString.cpp but printed error at compiling. ex) operator char*();
Can someone explain me the difference about adding "const" keyword at the end of char*()?
In this function:
void testFunc(const CMyString &strParam) {
cout << strParam << endl;
}
strParam is a const-reference to a CMystring, so you can only call const-qualified method on it:
// Not const-qualified overload
operator char*()
// const-qualified overload
operator char*() const
Within a const-qualified method, you cannot modify non-mutable attributes or call non-const methods, which guarantees that your object is not "modified" (at least it should not be from an external point of view).
Note that you should likely convert to const char* from the const-qualified method, otherwise it would be possible to do this:
void testFunc(const CMyString &strParam) {
// I am modifiying the underlying char array of const variable
static_cast<char*>(strParam)[0] = '\0';
}
So your conversion operator should be:
operator const char*() const;
Adding the const keyword at the end of a method forbids it to change any member values or call any non-const methods.
The only exception are mutable values, which can always be modified.
class A
{
int i1;
mutable int i2;
public:
void m() const
{
// i1 = 5 <- error
i2 = 5 // <- ok
}
}
#include<iostream>
#include<cstring>
#include<conio.h>
using namespace std;
class String
{
char *value;
int len;
public:
String()
{
len=0;
value=0;
}
~String() {}
String(char *s)
{
len=strlen(s);
value=new char[len+1];
strcpy(value,s);
}
String(String & s)
{
len=s.len;
value=new char[len+1];
strcpy(value,s.value);
}
friend String operator+(String obj1, String obj2)
{
String obj3;
obj3.len=obj1.len+obj2.len;
obj3.value=new char [obj3.len+1];
strcpy(obj3.value,obj1.value);
strcat(obj3.value,obj2.value);
return obj3;
}
friend String operator=(String obj1, String obj2)
{
String obj3;
strcpy(obj3.value,obj1.value);
strcat(obj3.value,obj2.value);
return obj3;
}
void display()
{ cout<<value<<endl; }
};
int main()
{
String s1("Bodacious ");
String s2("AllienBrain");
String s3;
s3=s1+s2;
s3.display();
getch();
}
As I am already operated the operator + in my code but i also want to overload the operator= to conactenate both the strings but this code shows no error when i overload the + operator but it shows the correct output i.e. Bodacious AllienBrain.
But when i overload the operator= it throws error so anyone tell me whats wrong with me?
More appropriate version of overloaded = operator would be as below:
class String
{
///...
String& operator=(const String& obj2)
{
if(this->value ){
delete this->value; // Free if existing
this->value = NULL;
}
len = obj2.len;
this->value = new char[len + 1];
strcpy(this->value, obj2.value);
return *this;
}
///
};
I try to overload relational operator in my own String class. This how the class look like: I store the string inside data which is an array.
class MyString
{
public:
//default constructor, create an empty string
MyString()
{
};
//create a string containing n copies of c
MyString(size_t n, char c) :data(new char[n]), data_length(n)
{
for (size_t i = 0; i < n; i++)
{
data[i] = c;
}
};
//create a string from a null-terminated array
MyString(const char *cp):data(new char[std::strlen(cp)]), data_length(std::strlen(cp))
{
//std::copy(cp, cp + std::strlen(cp), data);
std::copy(cp, cp + std::strlen(cp), stdext::checked_array_iterator<char*>(data, data_length));
}
//destructor
~MyString()
{
//free the array
delete[] data;
};
//relational operators
bool operator==(const char* lhs, const char* rhs)
{
}
private:
size_t data_length;
char* data;
};
Compiler says that cant overload more than 2 args so I understand that lhs must be implicit like this:
bool operator==(const char* rhs)
{
}
but, my problem is:
Supposing that I compare two strings which sit inside a null-terminated array how can I compare them in something like a loop?
I'm making my own string class but I got a problem reading the characters of the string while using strlen() to do that.
/****str.h****/
class str
{
private:
char *m_ptr;
unsigned int m_size;
//unsigned int m_capacity;
public:
str();
str (const char *);
str (const str &);
~str (){if (m_size != 0) delete [] m_ptr;};
char *data() {return m_ptr;};
//Sobrecarga de operadors.
str operator=(str);
friend std::ostream& operator<<(std::ostream &, str);
};
I got the error using the constructor initialized with a c-string constant.
/****str.cpp****/
//Default constructor.
str :: str ()
{
m_ptr = new char [1];
m_ptr[0] = '\0';
m_size = 0;
//m_capacity = 10;
}
str :: str(const char *sm_ptr)
{
m_size = strlen(sm_ptr); //HERE IS WHERE THE ERROR OCCURS.
m_ptr = new char[m_size + 1];
strcpy(m_ptr, sm_ptr); //Copies the C string pointed by source into the array pointed by destination, including the terminating null character
}
//Copy constructor.
str :: str(const str &right)
{
m_ptr = new char [right.m_size];
strcpy (m_ptr, right.m_ptr);
m_size = right.m_size;
}
str str::operator=(str right)
{
if (m_size != 0) delete [] m_ptr;
m_ptr = new char [right.m_size + 1];
strcpy(m_ptr, right.m_ptr);
m_size = right.m_size;
return *this;
}
std::ostream &operator<<(std::ostream &strm, str obj)
{
strm << obj.m_ptr;
return strm;
}
0x0053fdd0 {m_ptr=0xcccccccc m_size=3435973836 } str *
Changing the assignment operator declaration to
str& operator=(str&);
or even
const str& operator=(const str&);
would eliminate creation of temporary objects. Check out this article for more information on passing arguments to functions.
There are a couple of other issues. For example in default constructor you allocate memory but you don't set size so it's never going to be freed. Also, in copy constructor and assignment operator it's almost always a good idea to check for self-assignment.
I am trying to improve my understanding of the copy constructor and copy assign. operator
Here is a simple class that I came up with
class Bar
{
char* name;
int zip;
std::string address;
public:
Bar(const Bar& that)
{
//Copy constructor
size_t len = strlen(that.name + 1);
name = new char[len];
strcpy(name, that.name);
//Copy the zip
this->zip = that.zip;
//Copy the address
this->address = that.address;
}
Bar& operator=(const Bar& that)
{
//Assignment operator
if(this != &that)
{
//Copy the name
size_t len = strlen(that.name + 1);
name = new char[len];
strcpy(name, that.name);
//Copy the zip
this->zip = that.zip;
//Copy the address
this->address = that.address;
}
return *this;
}
};
My question is since the code in the copy constructor and copy assignment operator are the same does it make more sense to unify that into a deep copy method so that incase I add another member variable I dont have to add another line to the copy cnstr and copy assign. section ? Any suggestions ?
The "normal" way of doing things where you manage your own resources is a little different:
char* cppstrdup(const char*s, int len=0);
class Bar
{
char* name;
int zip;
std::string address;
public:
Bar(const Bar& that)
:name(nullptr),
zip(that->zip),
address(that->address)
{
name = cppstrdup(that.name); //done here for exception safety reasons
}
Bar(Bar&& that) //if you have C++11 then you'll want this too
:name(nullptr)
{
swap(*this,that);
}
~Bar() //you forgot the destructor
{
delete [] name;
}
Bar& operator=(Bar that) //this is called copy and swap.
{ //"that" is a copy (notice, no & above), and we simply swap
swap(*this,that);
return *this;
}
friend void swap(Bar& left, Bar& right)
{
using std::swap;
swap(left.name, right.name);
swap(left.zip, right.zip);
swap(left.address, right.address);
}
};
//uses new instead of malloc
inline char* cppstrdup(const char* s, int len)
{
if (s==0) return nullptr;
if (len==0) len = strlen(s);
char* r = new char[len+1];
strncpy(r, len+1, s);
r[len] = 0;
return r;
}
The benefits of this pattern is that it is much easier to get exception safety, often with the strong exception guarantee.
Of course, even more normal is to not use char* name, and obey the "Rule of Zero" isntead. In which case, it becomes VERY different:
class Bar
{
std::string name;
int zip;
std::string address;
public:
Bar() = default; //well, that's easy
};
Check COPY & SWAP idiom. In short - your logic goes into copy constructor and swap method and your assignment operator looks like:
Bar& operator=(const Bar& that)
{
Bar temp(that);
swap(*this, temp);
return *this;
}