I am currently working on an ADT called Text and I'm overloading the assignment operator. When I test the operator like so: assignText = alpha everything outputs okay. However, when I do assignText = assignText then two solid bars output instead.
assignText is declared as a Text object.
My overloaded assignment operator is as follows:
void Text::operator= (const Text& other) {
bufferSize = other.getLength();
buffer = new char[bufferSize];
buffer = other.buffer;
}
Where int bufferSize and char *buffer
Any tips or suggestions would be greatly appreciated. If anything else is needed, just let me know.
Other answers already have pointed out various issues with your operator implementation. Here I'll try to understand what's happening for you, i.e. why the code behaves as you observe. If this == &other, i.e. during self assignment, you take the current length as the size of a new buffer. That new buffer isn't initialized, so at that point it might contain random bytes. The last assignment is a no-op in case of a self-assigment. To sum things up:
void Text::operator= (const Text& other) {
bufferSize = other.getLength(); // Take length from current object
buffer = new char[bufferSize]; // Create new buffer, old buffer turns into a memory leak
buffer = other.buffer; // No-op as both are the same variable
}
So what this tells you is that you end up with a buffer of the current object size but with undefined content. In your case that undefined content happens to represent the vertical bars you mention.
To solve this, make sure to fix the assignment operator based on the suggestions from other answers and comments.
This is a memory leak. You're assigning two different pointers to buffer.
buffer = new char[bufferSize];
buffer = other.buffer;
Regarding the problem at hand, that's covered by the C++ FAQ: "Why should I worry about "self assignment"?". It's often a good idea to read the FAQ first. Or at least skim it.
When you absolutely have to implement a copy assignment operator, usually the copy-and-swap idiom will be good enough. It's also exception safe. Goes like this:
void swapWith( MyType& other ) throw()
{
// swap them members
}
void operator=( MyType other )
{
swapWith( other );
}
Here the copy constructor creates the formal argument copy, and any exception happens there, so that the copy constructor centralizes also the cleanup in case of copy failure. After that the contents of the two objects are swapped, and the copy object's destructor takes care of cleaning up what was this object's internal stuff. The void result type is not yet conventional, but as I see it it's not smart to waste both code and time on supporting expressions with side effects, which are evil.
Now, you can avoid all this, simply by using std::vector for your buffer.
So that's what I recommend, the really simple solution: use std::vector.
This is because your assignment operator is fundamentally unsafe. You need to break the old state as the last step. Consider
Text& Text::operator= (const Text& other) {
auto new_buffer = new char[other.bufferSize];
std::copy(other.buffer, other.buffer + other.bufferSize, new_buffer);
// Now consider whether or not you need to deallocate the old buffer.
// Then set the new buffer.
buffer = new_buffer;
bufferSize = other.bufferSize;
return *this;
}
This ensures that if any problems are encountered, it is guaranteed that the Text object is always valid- and as a bonus, is self-assignment-safe. All well-written assignment operators are self-assignment safe.
This is often tied with copy-and-swap, which copies, then swaps- guaranteeing safety.
This piece of code will work perfectly
Text& Text::operator=(const Text& other)
{
if(this == &other) /* checks for self assignment */
return *this;
if(NULL == other.buffer)
return *this;
if(NULL != buffer) /* assuming buffer=NULL in constructor */
{
delete [] buffer;
buffer = NULL;
}
bufferSize = other.getLength();
buffer = new char[bufferSize+1];
memset(buffer, 0, bufferSize+1);
memcpy(buffer, other.buffer, bufferSize);
return *this;
}
Try this..
After some thought and review, I think I got it. Given that I need to write a void function dictated by the header file that I must construct my implementation from. I took into consideration the comments from Drew Dormann, MvG, as well as others and came up with this:
void Text::operator= (const Text &other)
{
if (this != &other) // takes care of self-assignment
{
delete [] buffer; // delete the old buffer
bufferSize = other.bufferSize; // assign size variable
buffer = new char[bufferSize + 1]; // create new buffer
strcpy(buffer, other.buffer); // copy array
}
}
This works and I believe it is memory leak free.
Related
I have a class with assignment operator as below.
char *buff;
myString& operator= ( const myString& other )
{
cout << " myString::operator=\n";
if( this != &other ){
cout<<"my string ="<<endl;
delete [] buff;
length = other.length;
buff = new char[length];
my_strncpy( buff, other.buff, length );
}
return *this;
}
I am deleting memory for buff and allocating with length of new string. How can I handle any exception that happens during the allocation memory with new? How can I restore the value of buff to old values incase of exception?
There are two solutions to this. The first (best) is to use copy-and-swap:
myString& operator= ( myString other ) {
swap (*this, other);
return *this;
}
If allocation fails in the copy constructor, we'd never get to the swap, so there's no worry about overwriting our current state. For more, see What is copy-and-swap?
The other approach is to just make sure you only delete if it's safe. That is, do it after the new:
tmp_buff = new char[other.length];
// either that threw, or we're safe to proceed
length = other.length;
my_strncpy(tmp_buff, other.buff, length);
delete [] buff;
buff = tmp_buff;
Dealing with out-of-memory conditions is hard since there often is no easy failure path that can be used.
In your case, you could try creating the new buffer before deleting the old buffer but this may increase the likelyhood of running out of memory (OOM).
Ideally you should probably use the old buffer if it is big enough and only create a new buffer if the old one is too small. In such a case it is probably ill advised to use the old buffer in the event of OOM since it will be too small to store the string.
How can I handle any exception that happens during the allocation memory with new?
A try{} catch(){} should be used to catch exceptions thrown during allocation and object creation.
How can I restore the value of buff to old values incase of exception?
The key here is not really to restore the old values if an exception is thrown, but to first allocate and copy the contents, swapping pointers etc. and then delete the old object and memory. In this way if an exception is thrown, the current contents are left unchanged.
First allocate the memory to a temporary pointer, copy the data required, then swap the new pointer for the old one and delete the old data; akin to the copy-swap idiom. GotW (#59) also has a nice article on this here.
myString& operator= ( const myString& other )
{
if( this != &other ){
try {
char* temp_buff = other.length ? new char[other.length] : nullptr;
// I assume my_strncpy handle NULL pointers etc.
// If not, call it behind an if check for length and pointer validity
my_strncpy( temp_buff, other.buff, length );
std::swap(temp_buff, buff);
delete [] temp_buff;
length = other.length;
}
catch (std::bad_alloc& e) {
// deal with the bad_alloc...
}
catch (std::exception& e) {
// deal with the exception
}
}
return *this;
}
In general, out of memory conditions are significant, so just catching the exception may not always be ideal - it needs to be dealt with by the application as a whole, possibly even by the user for the entire system. A question to ask yourself is; what are you going to do with the exception, how are you going to recover from it?
A more general solution (I assume you really are focused on implementing the operator= in the current form) is to use a full blown copy-swap implementation.
class myString {
char* buff;
std::size_t length;
// ...
};
myString::myString(myString const& src) :
buff(src.length ? new char[src.length] : nullptr),
length(src.length)
{
if (length)
std::copy(src.buff, src.buff + length, buff);
}
myString::~myString()
{
delete [] buff;
length = 0;
}
void myString::swap(myString& rhs)
{
std::swap(rhs.buff, this->buff);
std::swap(rhs.length, this->length);
}
myString& myString::operator=(myString const& rhs)
{
if (this != &rhs) {
myString temp(rhs);
swap(temp);
}
return *this;
}
// the rest of the class implementation
//... non-member swap for addition utility
inline void swap(myString& lhs, myString& rhs)
{
lhs.swap(rhs);
}
I tried to search same questions, but not one helped me. When I run program I get the "A Buffer Overrun has occurred..." error.
Constr:
Player(char* n)
{
length = strlen(n);
name = new char[length+1];
for(unsigned int i(0); i < length; i++)
name[i] = n[i];
name[length] = '\0';
}
Destr:
~Player(void)
{
delete [] name;
}
I've NULL terminated string and don't get out of bounds, what is problem?
There's no obvious error in the code you've posted, but trying to manage dynamic memory by juggling raw pointers will almost inevitably lead to errors like this.
Perhaps you haven't correctly implemented or deleted the copy constructor and copy-assignment operator, per the Rule of Three. In that case, copying a Player object will give two objects with pointers to the same array; both of them will try to delete that array, giving undefined behaviour.
The simplest solution is to manage your string with a class designed for managing strings. Change the type of name to std::string, and then the constructor can simply be something like
explicit Player(std::string const & n) : name(n) {}
and there's no need to declare a destructor (or move/copy constructor/assignment operators) at all.
So... a solution by using an std::string has been provided, but let me give another solution, keeping your member variables intact.
The problem is this. Suppose you have this code somewhere:
Player p1("Bob"); // Okay
Player p2("Annie"); // Okay
p2 = p1; // Oops! (1)
Player p3(p1); // Oops! (2)
At (1), the method Player& Player::operator=(const Player&) is called. Since you didn't provide one, the compiler generates one for you. When it does, it simply assumes that it may copy over all member variables. In this case, it copies over Player::name and Player::length. So, we have p1.name == p2.name. Now when the destructor of p2 is called, the allocated memory pointed to by p2.name is deleted. Then when the destructor of p1 is called, the same memory will be deleted (since p1.name == p2.name)! That's illegal.
To fix this, you can write an assignment operator yourself.
Player& Player::operator = (const Player& other)
{
// Are we the same object?
if (this == &other) return *this;
// Delete the memory. So call the destructor.
this->~Player();
// Make room for the new name.
length = other.length;
name = new char[length + 1];
// Copy it over.
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
name[length] = '\0';
// All done!
return *this;
}
At (2), the same problem occurs. You do not have a copy constructor, so the compiler generates one for you. It will also assume that it may copy over all the member variables, so when the destructors get called, they'll try to delete the same memory again. To fix this, also write a copy constructor:
Player::Player(const Player& other)
{
if (this == &other) return;
length = other.length;
name = new char[length + 1];
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
}
At the end of the day you should use an std::string though.
I thought the following example would have the same result as std::map::emplace(), but it didn't:
struct MoveTest {
MoveTest():
var(true)
{
cout << "ctor: " << static_cast<void*>(&var) << endl;
}
MoveTest(MoveTest&& mt):
var(std::move(mt.var))
{
cout << "move ctor: " << static_cast<void*>(&var) << endl;
}
bool var;
};
int main() {
map<int, MoveTest> mtest;
mtest.insert(make_pair(1, MoveTest()));
return 0;
}
output:
ctor: 0x7fff12e4e19b
move ctor: 0x7fff12e4e194
move ctor: 0x25f3034
The MoveTest::var has a different address in each move. It doesn't look like a "move". What's wrong in my code or understanding?
For MoveTest as you've defined it, there's not really much a move ctor (or assignment operator) can optimize (or do in general--all it can really do is copy from source go destination).
Moving is mostly a big win when an object contains a pointer to a bunch of external memory that's (for example) allocated on the heap. In this case, the move ctor/assignment operator can basically do a shallow copy (i.e., just grab the pointer from the moved-from object) instead of a deep copy (copying all the data referred to by that pointer).
For example:
class move_tst {
int *buffer;
static const int size = 1024 * 1024;
public:
move_tst() {
buffer = new int[size];
std::iota(buffer, buffer + size, 0);
}
move_tst(move_tst const &other) {
buffer = new int[size];
std::copy_n(other.buffer, size, buffer);
}
#ifdef MOVE
move_tst(move_tst &&other) {
buffer = other.buffer;
other.buffer = nullptr;
}
#endif
~move_tst() {
delete [] buffer;
}
};
Caveat: I've used a raw invocation of new and a raw pointer here so nothing else gets involved and we wouldn't (for example) get move semantics courtesy of a smart pointer. For normal code under normal circumstances, you should not be using either (raw pointer or raw invocation of new, I mean).
Edit: as far as what std::move does, it doesn't actually do a move itself--it just signals that you no longer care about a value, so it's eligible to be the source of a move, even if that destroys its value.
For example, with the class above, you could do a test like:
for (int i = 0; i < 1000; i++) {
move_tst src;
move_tst dst =src;
}
...and compare it to:
for (int i = 0; i < 1000; i++) {
move_tst src;
move_tst dst = std::move(src);
}
Without the std::move, dst will be created as a copy of src, but with the std::move, it'll be able to do a move from src to dst.
Your log simply tells that you create a temporary MoveTest instance with the default constructor
MoveTest()
which is then copied elsewhere, inside a pair object
make_pair(1, MoveTest())
and copied again somewhere inside the map object
mtest.insert(make_pair(1, MoveTest()));
There are three constructor invocations for three distinct MoveTest instances, each with its own address. As already well explained, whether the constructors are move constructors or old style copy constructors doesn't matter.
Did you expect some of the copies to be optimized away? With nontrivial code in the constructors it becomes unlikely; in particular, reading &var is a good reason to avoid shortcuts.
I haven't been able to find a good answer to this question.
I'm working on a C++ program and I'm trying to implement a function named copy which takes a reference to another object as an argument. Then, it returns a deep copy of this object.
Some background on my project: the Scene class contains a dynamic array (called "Images") of pointers to either NULL or an instance of the Image class, which is not shown here - but works as it should (it inherits all of its methods from a third party library, EasyBMP)
The reason I'm doing this is to avoid duplicating code in two places, but it's very possible that I'm taking the wrong approach.
I call this function in my assignment operator:
Scene const & Scene::operator=(Scene const & source)
{
if (this != &source) {
clear();
copy(source);
}
return *this;
}
And my copy constructor:
Scene::Scene(Scene const & source)
{
copy(source);
}
Finally, my copy() method looks like this:
Scene const & Scene::copy(Scene const & source)
{
Scene res(source.Max);
for (int i=0; i<res.Max; i++)
{
delete res.Images[i];
if (source.Images[i] != NULL)
res.Images[i] = new Image(*(source.Images[i]));
else
res.Images[i] = NULL;
}
return res;
}
Currently, it does NOT work. One problem I can see is that I'm trying to return a variable that goes out of scope as soon as the copy function ends. I tried returning a reference before, but the compiler threw errors and this wouldn't help with the scope issue anyways.
But I'm not even sure that my logic is right, i.e. can you even do something like this in a constructor? Or should I just explicitly write out the code in the copy constructor and assignment operator (without implementing the helper method copy)?
I'm very new to C++ and pointers, so any guidance would be much appreciated.
There's a way easier and more idiomatic way to do what you want: the copy-and-swap idiom.
// N.B. Not tested, but shows the basic structure of the copy-and-swap idiom.
class Scene
{
public:
Scene(int)
{
// Initialize a pointer array of Images
}
~Scene()
{
// Get rid of our pointer array of Images
}
// Copy constructor
// N.B. Not exception safe!
Scene(const Scene& rhs) : imgPtrArray(new Image*[rhs.max])
{
// Perform deep copy of rhs
for (int i=0; i < rhs.max; ++i)
{
if (rhs.imgPtrArray[i] != 0)
imgPtrArray[i] = new Image(*(rhs.imgPtrArray[i]));
else
imgPtrArray[i] = 0;
}
}
// Copy assignment constructor
// When this is called, a temporary copy of Scene called rhs will be made.
// The above copy constructor will then be called. We then swap the
// members so that this Scene will have the copy and the temporary
// will destroy what we had.
Scene& operator=(Scene rhs)
{
swap(rhs);
return *this;
}
void swap(Scene& rhs)
{
// You can also use std::swap() on imgPtrArray
// and max.
Images** temp = imgPtrArray;
imgPtrArray = rhs.imgPtrArray;
rhs.imgPtrArray = temp;
int maxTemp = max;
max = rhs.max;
rhs.max = maxTemp;
}
private:
Images** imgPtrArray;
int max;
};
That being said, I highly recommend that you pick up a good introductory C++ book, which will cover the basics of implementing the copy constructor and copy assignment operators correctly.
Scene const & Scene::operator=(Scene const & source);
overloaded assignment operator copies the content of this to the argument received source. For copy there is no need to return any thing or to create a local object. Just make a member wise copy from this to source.
void Scene::copy(Scene const & source){
// Member wise copy from this to source
}
Rule of three should be helpful to better understand more about these.
So having
struct ResultStructure
{
ResultStructure(const ResultStructure& other)
{
// copy code in here ? using memcpy ? how???
}
ResultStructure& operator=(const ResultStructure& other)
{
if (this != &other) {
// copy code in here ?
}
return *this
}
int length;
char* ptr;
};
How to implement "copy constructor" and "assignment operator"? (sorry - I am C++ nube)
Update: sbi and others ask - why do I want to manually deal with raw memory? My answer is simple - In a students project I am part of now we use lots of C library's such as for example OpenCV OpenAL and FFmpeg and there are more to come. Currently using C++ we try to create a graph based direct show like cross platform library that would be helpful in live video broadcasting and processing. Our graph elements currently use char* and int pairs for data exchange. To cast data to subscribed elements we use raw memcpy now. I want to go further and make it possible for us to make our graph elements base C++ template. So that one graph element would be capable of of sharing current graph element data with other Graph elements and that data it shares would be a structure that would contain not one char* one int but any number of data fields and nearly any elements inside. So that is why I need to understand how to create a basic C++ structure that implements "copy constructor" and "assignment operator" for me to be capable to use new for us data casting algorithms like
void CastData(T item){
for(size_t i = 0 ; i < FuncVec.size(); i++){
T dataCopy = item;
FuncVec[i](dataCopy);
}
}
instead of currently used
void CastData(char * data, int length){
for(size_t i = 0 ; i < FuncVec.size(); i++){
char* dataCopy = new char[length];
memcpy(dataCopy, data, length);
FuncVec[i](dataCopy, length);
delete[] dataCopy;
}
}
You might want to explain why you want to manually deal with raw memory. I haven't done this in a long time, it's what std::string and std::vector where designed for:
struct ResultStructure
{
// nothing else needed
std::string data; // or std::vector<char>
};
But if you really need to do this the hard way (is this homework?), then be advised that it is, at first, surprisingly hard to get this right. For example, a naive implementation of the assignment operator might be like this:
// DON'T TRY THIS AT HOME!!
ResultStructure& ResultStructure::operator=(const ResultStructure& rhs)
{
delete[] ptr; // free old ressource
ptr = new char[rhs.length]; // allocate new resourse
std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr; // copy data
length = rhs.length;
}
If someone accidentally assigns an object to itself (which might happen if all you have is two references and you don't suspect them to refer to the same object), then this will fail fatally.
Also, what if new throws an exception? (It might throw std::bad_alloc if memory is exhausted.) Then we have already deleted the old data and have not allocated new data. The pointer, however, still points at where the old data used to be (actually, I think this is implementation-defined, but I have yet to see an implementation that changes a ptr upon deletion), and the class' destructor (you knew that class would need a destructor, right?) would then attempt to delete a piece of data at an address where no data is allocated. That's Undefined Behavior. The best you can hope for is that it crashes immediately.
The easiest way to do this is to employ the Copy-And-Swap idiom:
struct ResultStructure
{
ResultStructure(const ResultStructure& other)
: ptr(new char[rhs.length]), length(rhs.length)
{
std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr);
}
~ResultStructure() // your class needs this
{
delete[] ptr;
}
ResultStructure& operator=(ResultStructure rhs) // note: passed by copy
{
this->swap(rhs);
return *this
}
void swap(const ResultStruct& rhs)
{
using std::swap;
swap(length, rhs.length);
swap(ptr, rhs.ptr);
}
std::size_t length;
char* ptr;
};
Note that I have added a destructor, changed the assignment operator to pass the argument per copy (we need the copy constructor invoked to allocate memory), and added a swap member function. Per convention a swap() function never throws and is fast, preferably O(1).
I know that GMan's discussion of the Copy-And-Swap idiom is exhaustive enough to be and exhausting while mine is probably too terse for you, and you will likely not understand a lot of it at first, but try to persevere and to understand as much as possible.
If you use std::string, instead of char*, you would not even need to write operator= or copy-constructor. The compiler generated code would do your job very well.
But as a general solution (for some other scenario), use copy-and-swap idiom:
Copy-and-Swap Idiom
What is the copy-and-swap idiom?
Exceptional C++ by Herb Sutter has described these in great detail. I would recommend you to read items from this book. For the time being, you can read this article online:
Exception-Safe Generic Containers
The easy solution is to use a std::string instead of char* member.
Then the compiler-generated copy constructor and copy assignment operator just work.
As a rule, and especially as a novice, don't have raw pointer members.
Cheers & hth.,
As has been said, and as was recommending in the question this emanated from, you should probably reuse an existing container. At least the code would be right :)
For educational purposes though, let's examine this structure:
class Result
{
public:
private:
size_t length; // can't really be negative, right ?
char* data;
};
Here, we need explicit memory management. This implies, notably, following the Rule Of Three (say thanks to Fred)
Let's begin with actually building our object:
Result::Result(): length(0), data(0) {}
Result::Result(size_t l, char* d): length(0), data(0)
{
if (!d) { return; }
data = new char[l]; // this may throw, so we do it first
length = l;
memcpy(data, d, l);
}
Now we can implement the traditional operators:
// Swap
void Result::swap(Result& other)
{
using std::swap;
swap(length, other.length);
swap(data, other.data);
}
// Copy Constructor
Result::Result(Result const& other): length(0), data(0)
{
if (!other.length) { return; }
data = new char[other.length];
length = other.length;
mempcy(data, other.data, length);
}
// Assignemt Operator
Result& Result::operator=(Result other)
{
this->swap(other);
return *this;
}
// !IMPORTANT!
// Destructor
Result::~Result()
{
delete[] data; // use the array form of delete
// no need to test for nullity, it's handled
}
this is std::vector<char>'s job - or is this homework?
the vector would replace both length and the allocation behind ptr. the vector is the c++ idiom, and you'll not make friends with other c++ devs if you implement your classes like you've described. of course, there are corner cases, but standard containers such as vector are the default.
the vector knows how to copy chars as well as itself, and the implementations are optimized and tested.
here's how to explicitly implement copy ctor/assign using a vector:
struct ResultStructure {
ResultStructure(const ResultStructure& other) : d_chars(other.d_chars) {
}
ResultStructure& operator=(const ResultStructure& other) {
if (this != &other) {
this->d_chars = other.d_chars;
}
return *this;
}
std::vector<char> d_chars;
};
I think this should do the work:
struct ResultStructure
{
ResultStructure(const ResultStructure& other);
ResultStructure& operator=(const ResultStructure& other);
int length;
char* ptr;
};
ResultStructure::ResultStructure(const ResultStructure& other):length(other.length)
{
ptr = (char*)malloc(length);
memcpy(ptr, other.ptr, length);
}
ResultStructure& ResultStructure::operator=(const ResultStructure& other)
{
length = other.length;
ptr = (char*)malloc(length);
memcpy(ptr, other.ptr, length);
return *this;
}
Please remember about freeing ptr in destructor.
What is stored under ptr? If text, why not to use std::string? Anyway you can use std::vector. The constructors will be much easier then...
How is the memory to which ptr points allocated?
if using new, allocate with new, set length and then copy
other.length = length;
other.ptr = new char[length];
memcpy( other.ptr, ptr, length );
If you're allocating the memory with malloc, substitute a call to malloc in place of the call to new. If you're using some other memory scheme, figure it out. :)