You need create a copy constructor. This has to do the rule of 3/5. You are creating obj2
, which means a copy constructor is invoked, not the copy assignment operator.
Because you don't have a copy constructor, a "shallow" copy is made. This means that line
is copied by value. Since it's a pointer, both obj
and obj2
are pointing to the same memory. The first destructor gets called and erases that memory just fine. The second constructor gets called and a double delete occurs, causing your segmentation fault.
class MyClass {
public:
char *line = nullptr;
std::size_t size_ = 0; // Need to know the size at all times, can't
// rely on null character existing
const std::size_t MAX_SIZE = 256; // Arbitrarily chosen value
MyClass() { }
MyClass(const char *s) : size_(strlen(s)) {
if (size_ > MAX_SIZE) size_ = MAX_SIZE;
line = new char[size_];
strncpy(line, s, size_ - 1); // 'n' versions are better
line[size_ - 1] = '\0';
}
MyClass(const MyClass& other) : size_(other.size_) { // Copy constructor
line = new char[size_ + 1];
strncpy(line, other.line, size_);
line[size_] = '\0';
}
~MyClass() {
delete[] line;
line = nullptr;
}
MyClass& operator=(const MyClass &other) {
if (line == other.line) return *this; // Self-assignment guard
size_ = other.size_;
delete[] line;
line = new char[other.size_ + 1];
strncpy(line, other.line, size_);
line[size_] = '\0';
return *this;
}
int len(void) const { return size_; }
};
When dealing with C-Strings, you absolutely cannot lose the null character. The issue is that it's extremely easy to lose. You were also lacking a self-assignment guard in your copy assignment operator. That could have led to you accidentally nuking an object. I added a size_
member and used strncpy()
instead of strcpy()
because being able to specify a maximum number of characters is incredibly important in the case of losing a null character. It won't prevent damage, but it will mitigate it.
There's some other stuff that I did like using Default Member Initialization(as of C++11) and using a constructor member initialization list. A lot of this becomes unnecessary if you are able to use std::string
. C++ can be "C with classes" but it's worth taking the time to really explore what the language has to offer.
Something that a working copy constructor and destructor allows us to do is simplify our copy assignment operator using the "copy and swap idiom."
#include <utility>
MyClass& operator=(MyClass tmp) { // Copy by value now
std::swap(*this, tmp);
return *this;
}
Link to explanation.
MyClass obj1; MyClass obj2 = obj1;
will still segfault because you will callstrlen(obj1.line)
which isstrlen(NULL)
. As wouldMyClass obj1; obj1.len();
. – ComputerizeMyClass obj1; obj1.len();
It is undefined behavior to callstrlen
on a null pointer. – Hedron