I have an Array class written with RAII in mind (super simplified for the purpose of this example):
struct Array
{
Array(int size) {
m_size = size;
m_data = new int[m_size];
}
~Array() {
delete[] m_data;
}
int* m_data = nullptr;
int m_size = 0;
};
Then I have a function that takes a reference to an Array and does some operations on it. I use a temporary Array temp to perform the processing, because for several reasons I can't work directly with the reference. Once done, I would like to transfer the data from the temporary Array to the real one:
void function(Array& array)
{
Array temp(array.m_size * 2);
// do heavy processing on `temp`...
array.m_size = temp.m_size;
array.m_data = temp.m_data;
}
The obvious problem is that temp goes out of scope at the end of the function: its destructor is triggered, which in turn deletes the memory. This way array would contain non-existent data.
So what is the best way to "move" the ownership of data from an object to another?
What you want is move construction or move assignment for your array class, which saves you redundant copies. Also, it will help you to use std::unique_ptr which will take care of move semantics for allocated memory for you, so that you don't need to do any memory allocation directly, yourself. You will still need to pull up your sleeves and follow the "rule of zero/three/five", which in our case is the rule of five (copy & move constructor, copy & move assignment operator, destructor).
So, try:
class Array {
public:
Array(size_t size) : m_size(size) {
m_data = std::make_unique<int[]>(size);
}
Array(const Array& other) : Array(other.size) {
std::copy_n(other.m_data.get(), other.m_size, m_data.get());
}
Array(Array&& other) : m_data(nullptr) {
*this = other;
}
Array& operator=(Array&& other) {
std::swap(m_data, other.m_data);
std::swap(m_size,other.m_size);
return *this;
}
Array& operator=(const Array& other) {
m_data = std::make_unique<int[]>(other.m_size);
std::copy_n(other.m_data.get(), other.m_size, m_data.get());
return *this;
}
~Array() = default;
std::unique_ptr<int[]> m_data;
size_t m_size;
};
(Actually, it's a bad idea to have m_size and m_data public, since they're tied together and you don't want people messing with them. I would also template that class on the element type, i.e. T instead of int and use Array<int>)
Now you can implement your function in a very straightforward manner:
void function(Array& array)
{
Array temp { array }; // this uses the copy constructor
// do heavy processing on `temp`...
std::swap(array, temp); // a regular assignment would copy
}
if you insist on your example format, then you are missing a couple of things. you need to make sure that the temp array does not destroy memory reused by the non-temp array.
~Array() {
if (md_data != nullptr)
delete [] m_data;
}
and the function needs to do clean move
void function(Array& array)
{
Array temp(array.m_size * 2);
// do heavy processing on `temp`...
array.m_size = temp.m_size;
array.m_data = temp.m_data;
temp.m_data = nullptr;
temp.m_size = 0;
}
To make it more c++ like, you can do multiple things. i.e. in the array you can create member function to move the data from other array (or a constructor, or assign operator, ...)
void Array::move(Array &temp) {
m_size = temp.m_size;
temp.m_size = 0;
m_data = temp.m_data;
temp.m_data = 0;
}
in c++11 and higher you can create a move constructor and return your value from the function:
Array(Array &&temp) {
do your move here
}
Array function() {
Array temp(...);
return temp;
}
there are other ways as well.
Related
In c++ when classes contains dynamically allocated data it is usually reasonable to explicitly define copy constructor, operator= and destructor. But the activity of these special methods overlaps. More specifically operator= usually first does some destruction and then it does coping similar to the one in copy constructor.
My question is how to write this the best way without repeating the same lines of code and without the need for processor to do unnecessary work (like unnecessary copying).
I usually end up with two helping methods. One for construction and one for destruction. The first is called from both copy constructor and operator=. The second is used by destructor and operator=.
Here is the example code:
template <class T>
class MyClass
{
private:
// Data members
int count;
T* data; // Some of them are dynamicly allocated
void construct(const MyClass& myClass)
{
// Code which does deep copy
this->count = myClass.count;
data = new T[count];
try
{
for (int i = 0; i < count; i++)
data[i] = myClass.data[i];
}
catch (...)
{
delete[] data;
throw;
}
}
void destruct()
{
// Dealocate all dynamicly allocated data members
delete[] data;
}
public: MyClass(int count) : count(count)
{
data = new T[count];
}
MyClass(const MyClass& myClass)
{
construct(myClass);
}
MyClass& operator = (const MyClass& myClass)
{
if (this != &myClass)
{
destruct();
construct(myClass);
}
return *this;
}
~MyClass()
{
destruct();
}
};
Is this even correct?
And is it a good habit to split the code this way?
One initial comment: the operator= does not start by
destructing, but by constructing. Otherwise, it will leave the
object in an invalid state if the construction terminates via an
exception. Your code is incorrect because of this. (Note that
the necessity to test for self assignment is usually a sign that
the assignment operator is not correct.)
The classical solution for handling this is the swap idiom: you
add a member function swap:
void MyClass:swap( MyClass& other )
{
std::swap( count, other.count );
std::swap( data, other.data );
}
which is guaranteed not to throw. (Here, it just swaps an int
and a pointer, neither of which can throw.) Then you
implement the assignment operator as:
MyClass& MyClass<T>::operator=( MyClass const& other )
{
MyClass tmp( other );
swap( tmp );
return *this;
}
This is simple and straight forward, but any solution in which
all operations which may fail are finished before you start
changing the data is acceptable. For a simple case like your
code, for example:
MyClass& MyClass<T>::operator=( MyClass const& other )
{
T* newData = cloneData( other.data, other.count );
delete data;
count = other.count;
data = newData;
return *this;
}
(where cloneData is a member function which does most of what
your construct does, but returns the pointer, and doesn't
modify anything in this).
EDIT:
Not directly related to your initial question, but generally, in
such cases, you do not want to do a new T[count] in
cloneData (or construct, or whatever). This constructs all
of the T with the default constructor, and then assigns them.
The idiomatic way of doing this is something like:
T*
MyClass<T>::cloneData( T const* other, int count )
{
// ATTENTION! the type is a lie, at least for the moment!
T* results = static_cast<T*>( operator new( count * sizeof(T) ) );
int i = 0;
try {
while ( i != count ) {
new (results + i) T( other[i] );
++ i;
}
} catch (...) {
while ( i != 0 ) {
-- i;
results[i].~T();
}
throw;
}
return results;
}
Most often, this will be done using a separate (private) manager
class:
// Inside MyClass, private:
struct Data
{
T* data;
int count;
Data( int count )
: data( static_cast<T*>( operator new( count * sizeof(T) ) )
, count( 0 )
{
}
~Data()
{
while ( count != 0 ) {
-- count;
(data + count)->~T();
}
}
void swap( Data& other )
{
std::swap( data, other.data );
std::swap( count, other.count );
}
};
Data data;
// Copy constructor
MyClass( MyClass const& other )
: data( other.data.count )
{
while ( data.count != other.data.count ) {
new (data.data + data.count) T( other.date[data.count] );
++ data.count;
}
}
(and of course, the swap idiom for assignment). This allows
multiple count/data pairs without any risk of loosing exception
safety.
I don't see any inherent problem with that, as long as you make sure not to declare construct or destruct virtual.
You might be interested in chapter 2 in Effective C++ (Scott Meyers), which is completely devoted to constructors, copy operators and destructors.
As for exceptions, which your code is not handling as it should, consider items 10 & 11 in More effective C++ (Scott Meyers).
Implement the assignment by first copying the right-hand side and then swapping with that. This way you also get exception safety, which your code above doesn't provide. You could end up with a broken container when construct() fails after destruct() succeeded otherwise, because the member pointer references some deallocated data, and on destruction that will be deallocated again, causing undefined behaviour.
foo&
foo::operator=(foo const& rhs)
{
using std::swap;
foo tmp(rhs);
swap(*this, tmp);
return *this;
}
I have a class, Somec, with a vector of pointers to items. Each item in the vector is from another class type, Myitem.
I want to make a copy function for Somec, but I am having some problems.
Also, I have a copy function for Myitem, do I have to use it in the copy function for Somec?
What I have tried so far:
class Somec {
string Name;
std::vector<Myitem*> Items;
public:
Somec(const Somec& somec); // my copy function
};
/// this the clas to my item :
class Myitem {
Itemk itemk;
public:
// ... some fuction
};
// now I want to make a copy function for Somec
Somec::Somec(const Somec& somec) {
int size = somec.Items.size();
this->Items = new (vector*) (size); // i get an error here
this->Name = somec.Name;
for(int i = 0; i < size; i++) {
*Items.at(i) = *somec.Items.at(i);
}
}
UPDATE: I made the copy function just like Remy Lebeau told me to, but when I try to test this function the code stops working. This is how I am testing it:
class Somec {
string Name;
std::vector<Myitem*> Items;
public:
Somec(const Somec& somec); // my copy function
};
Somec::Somec(const Somec& somec) { // new copy function for somec
int size = somec.Items.size();
this->Name = somec.Name;
for(int i = 0; i < size; i++) {
Items.push_back(new Myitem(*somec.Items[i]));
}
}
// create item function
void Somec::createitem(char* name, const int& Time, const int& level) {
try{
Myitem* ORitem=new Myitem(name, Time, level);
Somec::Items.push_back(ORitem);
}
catch(MemoryProblemException& error) {
throw SomecMemoryProblemException();
}
}
std::vector<Myitem*> Somec::getAllItems() const {
return Items;
}
/// this the class to my item :
class Myitem {
Itemk itemk;
public:
// ... some fuction
};
// my copy function to MYitem
Myitem::Myitem(const Myitem& item){
Myitem* currie = Myitem::clone_item();
curritem->item = itemCopy(item.item);
if (!curritem->item) {
throw itemMemoryProblemException();
return;
}
}
void testCopy() {
Somec somec("name1")
somec.createitem((char*) "item1", 30, 1, 2);
Somec temp(somec);
int x=0;
if ( std::equal(somec.getAllItems().begin() + 1, somec.getAllItems().end(), somec.getAllItems().begin()) ) {
x++;
}
ASSERT_TRUE(x==1);
}
What is my problem? I mean, I did the copy function, I think it is true. But why does the code stop working? Am I testing this in the right way?
My createitem function, I am 100% sure about it, actually.
I am just trying to add items to the the vector in Somec and check if this happened correctly. I learned that in C++, we need a copy constructor, so I wrote one, and thought about testing it since this is my first time doing one.
If you want to copy elements from the vector of the other instance to the vector of your current instance in the copy constructor, then simply do this
class Somec {
string Name;
std::vector<Myitem> Items;
public:
Somec(const Somec& somec); // my copy function
};
i.e. make the vector of values instead of pointers.
If for some reason you have to have to work with pointers, because maybe the copy constructor is deleted or something similar. Then still use this approach, but just don't copy the contents of in the copy constructor, let them get constructed and then assign them
Somec(const Somec& somec) {
this->Items.resize(somec.Items.resize());
for (std::size_t i = 0; i < somec.Items.size(); ++i) {
this->Items[i] = somec.Items[i];
}
// or simply
this->Items = somec.Items;
}
The default compiler-generated copy constructor already does exactly what your copy constructor is doing manually. So it is redundant code.
However, assuming Somec owns the items, then you do need to define a custom copy constructor, and it needs to allocate new Myitem objects and copy the existing data into them, eg:
Somec::Somec(const Somec& somec) {
this->Name = somec.Name;
std::size_t size = somec.Items.size();
this->Items.reserve(size);
for(std::size_t i = 0; i < size; i++) {
Items.push_back(new Myitem(*somec.Items[i]));
}
}
You will have to do something similar in an overriden operator= as well. You can utilize the copy constructor to help you:
Somec& Somec::operator=(const Somec& somec) {
if (&somec != this) {
Somec temp(somec);
std::swap(this->Name, temp.Name);
std::swap(this->Items, temp.Items);
}
return *this;
}
And, of course, make sure you free the owned items in the destructor:
Somec::~Somec() {
std::size_t size = somec.Items.size();
for(std::size_t i = 0; i < size; i++) {
delete Items[i];
}
}
See Rule of Three for more details about why this trio is needed.
I have a class
class Invader
{
public:
Invader();
~Invader();
public:
void Init(InvaderTypes invadertype, CIw2DImage *AlienImage);
void Update(float dt);
void Render();
void SetAlienImage(CIw2DImage *image){ ImageAlien = image; }
void setVisible(bool show) { Visible = show; }
bool isVisible() const { return Visible; }
Iw2DSceneGraph::CSprite *AlienSprite;
Iw2DSceneGraph::CAtlas *AlienAtals;
CIw2DImage *ImageAlien;
std::list<Bullet*> *Bullets;
CIwFMat2D Transform; // Transform matrix
bool Visible; // Sprites visible state
bool Canfire;
};
void Invader::Init(InvaderTypes invadertype, CIw2DImage *AlienImage)
{
if (invadertype == InvaderTypes::TOP_ALIEN)
{
//SetAlienImage(AlienImage);
mImageAlien = AlienImage;
// Create EnemyTop atlas
int frame_w = (int)(mImageAlien->GetWidth() / 2);
int frame_h = (int)(mImageAlien->GetHeight());
AlienAtals = new CAtlas(frame_w, frame_h, 2, mImageAlien);
AlienSprite = new CSprite();
AlienSprite->m_X = 0;
AlienSprite->m_Y = 0;
AlienSprite->SetAtlas(AlienAtals);
AlienSprite->m_W = (float)AlienAtals->GetFrameWidth();
AlienSprite->m_H = (float)AlienAtals->GetFrameHeight();
AlienSprite->m_AnchorX = 0.5;
AlienSprite->SetAnimDuration(2);
}
else if (invadertype == InvaderTypes::MIDDLE_ALIEN)
{
}
else if (invadertype == InvaderTypes::LAST_ALIEN)
{
}
Visible = true;
Bullets = new std::list<Bullet*>();
Canfire = true;
}
Invader::Invader()
{
}
Invader::Invader(const Invader&other)
{
AlienAtals = new CAtlas();
AlienSprite = new CSprite();
*AlienAtals = *other.AlienAtals;
*AlienSprite = *other.AlienSprite;
}
I try to initialize it by:
list<Invader> *invaders = new list<Invader>();
int spacing = 10;
for (int i = 0; i < 5; i++)
{
Invader invader;
invader.Init(TOP_ALIEN, gameResources->getAlienImageTop());
invader.AlienSprite->m_X = 50 + spacing;
invaders->push_back(invader);
spacing += 50;
}
After pushing the object invader to the list, at the end the invaders list holds pointers that are not initialized. All the pointers got lost the references. I wonder why ?
The problem, I assume, is what happens in ~Invader(). Let's simplify the example a ton:
struct A {
int* p;
A() { p = new int(42); }
~A() { delete p; }
};
A just manages a pointer. When an A goes out of scope, that pointer gets deleted. Now, what happens when we do this:
list<A> objs;
{
A newA;
objs.push_back(newA);
// newA deleted here
}
// objs has one element... but its pointer has been deleted!
The problem is that copying A (which push_back() does) just performs a shallow copy: we copy our pointer. But since A manages its own memory, we need to do a deep copy. That is:
A(const A& rhs)
: p(new int(*(rhs.p)))
{ }
That way, the copied A won't double delete the same pointer. With C++11, this can be much more easily managed with just:
struct A {
std::shared_ptr<int> p;
A() { p = std::make_shared<int>(42); }
~A() = default; // this line not even necessary
};
Here, copying A will copy the shared_ptr and both copies of A will have a valid object to point to. If you can't use C++11, you can still use boost::shared_ptr<T> for all your memory management needs. And if you can't use that, then you have to write a copy constructor that does a full copy of all your pointer elements.
Or, the simplest solution, would be to just make your container have pointers:
list<A*> objs;
objs.push_back(new A);
Then the "shallow copy" is the right thing to do, and all you need to do is remember to delete everything in the container at the end.
Your list contains Invader objects. When you store them in the list, the Invader copy-constructor is called and a copy of the variable invader is stored in the list.
Unless you define a copy-constructor, it will be default simply do a shallow-copy of your object. This may not be what you want. You should write an explicit copy-constructor to make sure that the Invader is copied correctly.
Another solution would be to dynamically allocate Invader objects using the new keyword and storing pointers in the list. If you do that, be careful to make sure that you call delete on the Invader objects in the list when you are done with them.
After making a lot of changes to a project, I created an error that took me quite a while to track down.
I have a class which contains a dynamically allocated array. I then create a dynamic array of this class. I can then delete[] that array. But, if I replace an item in the array before deleting it, it causes an error. In debug mode, it gives an assertion message from dbgdel.cpp "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)". Here is a small program to demonstrate.
class SomeClass {
public:
int *data;
SomeClass() {
data = nullptr;
}
SomeClass(int num) {
data = new int[num];
}
~SomeClass() {
if (data != nullptr) {
delete[] data;
}
}
};
int main(int argc, char *args[]) {
SomeClass *someArray = new SomeClass[10];
//If you comment this out, there is no error. Error gets thrown when deleting
someArray[0] = SomeClass(10);
delete[] someArray;
return 0;
}
I'm curious, why does this happen? When the item in the array gets replaced, its destructor gets called. Then the new item allocates its data in a location separate from the array. Then delete[] calls the destructors of all the objects in the array. When the destructors get called, they should delete the item's data array. I can't imagine what the problem is, but I'd like if someone could explain.
Your class is broken: It has a non-trivial destructor, but you do not define copy constructors and copy assignment operators. That means that the class cannot be correctly copied or assigned-to (since the destructible state is not copied or assigned appropriately), as you are noticing in your example code.
You can either make your class uncopiable (in which case your code won't compile any more), or move-only, in which case you need to define move construction and move-assignment, or properly copyable by implementing a deep copy of the data.
Here's how, add the following definitions:
Non-copyable:
SomeClass(SomeClass const &) = delete;
SomeClass & operator(SomeClass const &) = delete;
Moveable-only:
SomeClass(SomeClass const &) = delete;
SomeClass(SomeClass && rhs) : data(rhs.data) { rhs.data = nullptr; }
SomeClass & operator(SomeClass const &) = delete;
SomeClass & operator(SomeClass && rhs) {
if (this != &rhs) { delete data; data = rhs.data; rhs.data = nullptr; }
return *this;
}
Copyable:
SomeClass(SomeClass const & rhs) : ptr(new int[rhs->GetSizeMagically()]) {
/* copy from rhs.data to data */
}
SomeClass & operator(SomeClass const & rhs) {
if (this == &rhs) return *this;
int * tmp = new int[rhs->GetSizeMagically()];
/* copy data */
delete data;
data = tmp;
}
// move operations as above
The upshot is that the nature of the destructor determines the invariants of the class, because every object must be consistently destructible. From that you can infer the required semantics of the copy and move operations. (This is often called the Rule of Three or Rule of Five.)
Kerrek SB s answer is great. I just want to clarify that in your code memory is freed twice.
This code
someArray[0] = SomeClass(10);
is the same as this
SomeClass temp(10);
someArray[0] = temp; //here temp.data is copied to someArray[0].data
Then ~SomeClass() is called for temp and data is freed first time.
Here
delete[] someArray;
~SomeClass() is called for someArray[0] and data is freed second time.
In c++ when classes contains dynamically allocated data it is usually reasonable to explicitly define copy constructor, operator= and destructor. But the activity of these special methods overlaps. More specifically operator= usually first does some destruction and then it does coping similar to the one in copy constructor.
My question is how to write this the best way without repeating the same lines of code and without the need for processor to do unnecessary work (like unnecessary copying).
I usually end up with two helping methods. One for construction and one for destruction. The first is called from both copy constructor and operator=. The second is used by destructor and operator=.
Here is the example code:
template <class T>
class MyClass
{
private:
// Data members
int count;
T* data; // Some of them are dynamicly allocated
void construct(const MyClass& myClass)
{
// Code which does deep copy
this->count = myClass.count;
data = new T[count];
try
{
for (int i = 0; i < count; i++)
data[i] = myClass.data[i];
}
catch (...)
{
delete[] data;
throw;
}
}
void destruct()
{
// Dealocate all dynamicly allocated data members
delete[] data;
}
public: MyClass(int count) : count(count)
{
data = new T[count];
}
MyClass(const MyClass& myClass)
{
construct(myClass);
}
MyClass& operator = (const MyClass& myClass)
{
if (this != &myClass)
{
destruct();
construct(myClass);
}
return *this;
}
~MyClass()
{
destruct();
}
};
Is this even correct?
And is it a good habit to split the code this way?
One initial comment: the operator= does not start by
destructing, but by constructing. Otherwise, it will leave the
object in an invalid state if the construction terminates via an
exception. Your code is incorrect because of this. (Note that
the necessity to test for self assignment is usually a sign that
the assignment operator is not correct.)
The classical solution for handling this is the swap idiom: you
add a member function swap:
void MyClass:swap( MyClass& other )
{
std::swap( count, other.count );
std::swap( data, other.data );
}
which is guaranteed not to throw. (Here, it just swaps an int
and a pointer, neither of which can throw.) Then you
implement the assignment operator as:
MyClass& MyClass<T>::operator=( MyClass const& other )
{
MyClass tmp( other );
swap( tmp );
return *this;
}
This is simple and straight forward, but any solution in which
all operations which may fail are finished before you start
changing the data is acceptable. For a simple case like your
code, for example:
MyClass& MyClass<T>::operator=( MyClass const& other )
{
T* newData = cloneData( other.data, other.count );
delete data;
count = other.count;
data = newData;
return *this;
}
(where cloneData is a member function which does most of what
your construct does, but returns the pointer, and doesn't
modify anything in this).
EDIT:
Not directly related to your initial question, but generally, in
such cases, you do not want to do a new T[count] in
cloneData (or construct, or whatever). This constructs all
of the T with the default constructor, and then assigns them.
The idiomatic way of doing this is something like:
T*
MyClass<T>::cloneData( T const* other, int count )
{
// ATTENTION! the type is a lie, at least for the moment!
T* results = static_cast<T*>( operator new( count * sizeof(T) ) );
int i = 0;
try {
while ( i != count ) {
new (results + i) T( other[i] );
++ i;
}
} catch (...) {
while ( i != 0 ) {
-- i;
results[i].~T();
}
throw;
}
return results;
}
Most often, this will be done using a separate (private) manager
class:
// Inside MyClass, private:
struct Data
{
T* data;
int count;
Data( int count )
: data( static_cast<T*>( operator new( count * sizeof(T) ) )
, count( 0 )
{
}
~Data()
{
while ( count != 0 ) {
-- count;
(data + count)->~T();
}
}
void swap( Data& other )
{
std::swap( data, other.data );
std::swap( count, other.count );
}
};
Data data;
// Copy constructor
MyClass( MyClass const& other )
: data( other.data.count )
{
while ( data.count != other.data.count ) {
new (data.data + data.count) T( other.date[data.count] );
++ data.count;
}
}
(and of course, the swap idiom for assignment). This allows
multiple count/data pairs without any risk of loosing exception
safety.
I don't see any inherent problem with that, as long as you make sure not to declare construct or destruct virtual.
You might be interested in chapter 2 in Effective C++ (Scott Meyers), which is completely devoted to constructors, copy operators and destructors.
As for exceptions, which your code is not handling as it should, consider items 10 & 11 in More effective C++ (Scott Meyers).
Implement the assignment by first copying the right-hand side and then swapping with that. This way you also get exception safety, which your code above doesn't provide. You could end up with a broken container when construct() fails after destruct() succeeded otherwise, because the member pointer references some deallocated data, and on destruction that will be deallocated again, causing undefined behaviour.
foo&
foo::operator=(foo const& rhs)
{
using std::swap;
foo tmp(rhs);
swap(*this, tmp);
return *this;
}