I have a class like the following:
class A {
SuperHugeClass* s;
public:
A(){ s = new SuperHugeClass(); }
};
Because SuperHugeClass takes a lot of memory, I'm fine with the shallow copying provided by the default constructor and assignment operator. However, I also don't want to leak memory, so I need to delete s, but I have to be careful about it because otherwise I'll delete it more than once.
One way of doing this is by refcounting s as follows:
class A {
int* refcount;
SuperHugeClass* s;
public:
A(){
refcount = new int(1);
s = new SuperHugeClass();
}
A(const A& other) : refcount(other.refcount), s(other.s) {
(*refcount)++;
}
~A() {
(*refcount)--;
if (!(*refcount)) {
delete refcount;
delete s;
}
}
friend void swap(const A& a, const A& aa) {
std::swap(a.refcount, aa.refcount);
std::swap(a.s, aa.s);
}
A& operator=(A other) {
swap(*this, other);
return (*this);
}
};
This is the first time I've needed to do something like this, but it seems to me that this should be pretty standard and so there should be a 'canonical' solution. Are there any other ways of doing this? Thanks!
Use std::shared_ptr
class A {
std::shared_ptr<SuperHugeClass> s;
public:
A()
: s(new SuperHugeClass())
{
}
};
and thats it. Default generated copy constructor/assignment operator/destructor do just what you need.
Use std/boost::shared_ptr instead of your ref-counted pointer.
Related
i need deep copy in my project and for now i memcpy the srcObj into destObj then
if destObj owns pointer members, i just create all the obj and do this method recursively
here's the pseudo:
class B
{
public:
B(int id_) : id(id_) {};
int id = 0;
};
class A
{
public:
vector<B*> vecInt;
B objB = 111;
A()
{
vecInt.push_back(new B(1));
vecInt.push_back(new B(2));
vecInt.push_back(new B(3));
}
A(const A& rhs)
{
memcpy(this, &rhs, sizeof(A));
for (auto i = 0; i < rhs.vecInt.size(); i++)
{
auto ptrTmp = new B(rhs.vecInt[i]->id);
cout << "00000000000 " << rhs.vecInt[i] << endl;;
this->vecInt[i] = ptrTmp;
cout << "11111111111 " << ptrTmp << endl;;
cout << "22222222222 " << rhs.vecInt[i] << endl;;
}
}
};
here's the issue, every time i assign this->vecInt[i] within the loop, the rhs.vecInt[i] changes too and they both indicate to one address, i have no idea why this happened.
appreciate any help.
The memcpy() is absolutely wrong and needs to be removed. It is corrupting your A object’s data members. It may work for the objB member, but definitely not the vecInt member.
But, even with thae memcpy() removed, you would still have undefined behavior as you are trying to assign to vector elements that don’t exist yet. To deep-copy a vector of pointers, you have no choice but to clone each dynamic B object one at a time and add it to the new vector.
The correct way to implement your copy constructor should look more like this instead:
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto *elem : rhs.vecInt)
{
vecInt.push_back(new B(*elem));
}
}
You also need to add a destructor, move constructor, copy assignment operator, and move assignment operator, per the Rule of 3/5/0:
class A
{
public:
vector<B*> vecInt;
B objB = 111;
A()
{
vecInt.push_back(new B(1));
vecInt.push_back(new B(2));
vecInt.push_back(new B(3));
}
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto *elem : rhs.vecInt)
{
vecInt.push_back(new B(*elem));
}
}
A(A&& rhs) : vecInt(move(rhs.vecInt)), objB(move(rhs.objB)) {}
~A()
{
for(auto *elem : vecInt)
delete elem;
}
A& operator=(A rhs)
{
vecInt.swap(rhs.vecInt);
objB.id = rhs.objB.id;
return *this;
}
};
That being said, consider using std::vector<std::unique_ptr<B>> instead of std::vector<B*>. That will eliminate the need for an explicit destructor. Don’t use new/delete in modern C++ if you can avoid it.
class A
{
public:
vector<unique_ptr<B>> vecInt;
B objB = 111;
A()
{
vecInt.push_back(make_unique<B>(1));
vecInt.push_back(make_unique<B>(2));
vecInt.push_back(make_unique<B>(3));
}
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto &elem : rhs.vecInt)
{
vecInt.push_back(make_unique<B>(*elem));
}
}
A(A&& rhs) : vecInt(move(rhs.vecInt)), objB(move(rhs.objB)) {}
~A() = default;
A& operator=(A rhs)
{
vecInt.swap(rhs.vecInt);
objB.id = rhs.objB.id;
return *this;
}
};
Even better, just use std::vector<B> instead, and let the compiler handle everything else for you:
class A
{
public:
vector<B> vecInt;
B objB = 111;
A()
{
vecInt.emplace_back(1);
vecInt.emplace_back(2);
vecInt.emplace_back(3);
}
};
I study for an exam in c++
and I was asked if in this code the d'tor of the class should use delete[] instead delete:
template <class T>
class ClonePtr
{
private:
T* ptr;
public:
explicit ClonePtr(T* p = nullptr) : ptr(p) {}
~ClonePtr() { if(ptr!=nullptr) delete []ptr; }
ClonePtr(const ClonePtr& other) : ptr(nullptr)
{
*this = other;
}
ClonePtr(ClonePtr&& other) : ptr(other.ptr)
{
other.ptr = nullptr;
}
ClonePtr& operator=(const ClonePtr& other)
{
if (this != &other)
{
delete ptr;
if (other.ptr != nullptr)
ptr = other.ptr->clone();
}
return *this;
}
T& operator*() const { return *ptr; }
};
The right answer to the question is yes,
but why is that?
+I have two more little question regarding this code:
It says in the exam that type T must be a class and cannot be a primitve type.
Why is that?
On the other hand, I tried writing this code:
class A
{
private:
int x;
public:
A(int x = 8) : x(x) { };
};
int main()
{
ClonePtr<int> h(new A(7));
}
and got a compiler error: C2664 "cannot convert argument 1 from A* to T*"
I will very much appreciate your help.
thanks :)
There is no way for us to answer this definitely one way or the other, because we can't see what kind of pointer is being passed to ClonePtr(T*), or what kind of pointer is being returned by clone(). This class is not doing any memory allocations of its own.
IF both pointers are being allocated with new and not by new[], then delete would be the correct answer, not delete[].
IF both pointers are being allocated with new[] and not by new, then delete[] would be the correct answer, not delete.
For that matter, since this code is taking ownership of memory allocated by something else, and it is clear by this class' use of nullptr and a move constructor (but not a move assignment operator!) that you are using C++11 or later, so you should be utilizing proper ownership semantics via std::unique_ptr or std::shared_ptr, not using a raw pointer at all.
Especially since your copy assignment operator is not setting ptr to nullptr after delete ptr; if other.ptr is nullptr, leaving *this in a bad state. Using proper ownership semantics would have avoided that.
Try this instead:
UPDATE: now you have posted additional code (that doesn't compile, since an A* can't be assigned to an int*, and A does not implement clone()) showing ptr being set to an object allocated with new, so the correct answer is:
delete MUST be used, not delete[].
So the "right answer" to your exam is wrong.
But proper ownership semantics would be even better, eg:
#include <memory>
template <class T>
class ClonePtr
{
private:
std::unique_ptr<T> ptr;
public:
ClonePtr(std::unique_ptr<T> p) : ptr(std::move(p)) {}
ClonePtr& operator=(ClonePtr other)
{
ptr = std::move(other.ptr);
return *this;
}
T& operator*() const { return *ptr; }
};
class A
{
private:
int x;
public:
A(int x = 8) : x(x) { };
std::unique_ptr<A> clone() const
{
return std::make_unique<A>(x);
// or: prior to C++14:
// return std::unique_ptr<A>(new A(x));
}
};
int main()
{
ClonePtr<A> h(std::make_unique<A>(7));
// or, prior to C++14:
// ClonePtr<A> h(std::unique_ptr<A>(new A(7)));
}
I am trying to implement an operator= in C++ for an object which has as a member a pointer to a user defined type which also has dynamic memory allocated in it.
So given the code below, how would on implement a correct operator= for B? What I am after is how is the dynamic memory in A copied to the new B object?
Any help would be much appreciated.
Thank you
class A
{
int a;
int* b;
public:
A()
{
a = 1;
b = new int [20];
}
};
class B
{
A* a;
public:
B()
{
a = new A;
}
}
For starters you should define at least copy constructor, copy assignment operator and destructor for the class A. Then it is simple to define the copy assignment operator for the class B.
For example
#include <iostream>
#include <algorithm>
class A
{
static const size_t N = 20;
int a;
int* b;
public:
A()
{
a = 1;
b = new int [N]();
}
A( const A &a ) : a( a.a ), b( new int [N] )
{
std::copy( a.b, a.b + N, this->b );
}
A & operator =( const A &a )
{
if ( &a != this )
{
this->a = a.a;
int *p = new int[N];
std::copy( a.b, a.b + N, p );
delete [] this->b;
this->b = p;
}
return *this;
}
~A()
{
delete []b;
}
};
class B
{
A* a;
public:
B() : a( new A )
{
}
// copy constructor
~B()
{
delete a;
}
B & operator =( const B &b )
{
if ( this != &b )
{
*this->a = *b.a;
}
return *this;
}
};
int main()
{
B b1;
B b2;
b1 = b2;
}
Pay attention to that in the copy assignment operator at first created a new array before deleting the old one. This allows to keep the stable state of the assignable object if an exception will occur.
At very first: Have a look at the rule of three, it is an absolute must go in given case. Consider, too, the rule of five, while not mandatory, you'll leave out a great optimisation opportunity...
The destructor would now delete[] the array (leaving this part to you...), a copy constructor would then do exactly that: (deep) copy the data:
A::A(A const& other)
: a(other.a), b(new int[20]) // assuming you have a fixed size for those arrays;
// better: introduce a constant for to avoid magic
// numbers in code!
{
// you created a new array, but yet need to fill it with the others value
std::copy(other.b, other.b + 20, b);
}
OK, first step. Using the copy and swap idiom, the assignment operator gets pretty simple:
A& operator=(A other) // YES, no reference! This will invoke the copy (or move!)
// constructor of your class!
{
swap(*this, other); // you'll need to implement it yet!
return *this;
// at this point, the destructor of other will clean up data that was potentially
// contained in *this before...
}
Finally the move constructor:
A::A(A&& other)
: a(0), b(nullptr)
{
swap(*this, other);
// again swapping??? well, sure, you want the data from other to be contained
// in *this, and we want to leave other in some kind of valid state, which the
// nullptr is fine for (it's fine to delete[] a null pointer, so you don't even
// need to check in the destructor...)
}
And now up to you: class B analogously...
Side note: you get away a bit cheaper by use of a smart pointer (std::unique_ptr), it will allow you to default destructor and move constructor + assignment operator, solely copy constructor and assignment operator need to be implemented explicitly (std::unique_ptr is not copiable...).
I give the following example to illustrate my question:
class Abc
{
public:
int a;
int b;
int c;
};
class Def
{
public:
const Abc& abc_;
Def(const Abc& abc):abc_(abc) { }
Def& operator = (const Def& obj)
{
// this->abc_(obj.abc_);
// this->abc_ = obj.abc_;
}
};
Here I do not know how to define the copy assignment operator. Do you have any ideas? Thanks.
references cannot be assigned to. You need something that can. A pointer would work, but they're very abusable.
How about std::reference_wrapper?
#include <functional>
class Abc
{
public:
int a;
int b;
int c;
};
class Def
{
public:
std::reference_wrapper<const Abc> abc_;
Def(const Abc& abc):abc_(abc) { }
// rule of zero now supplies copy/moves for us
// use the reference
Abc const& get_abc() const {
return abc_.get();
}
};
A reference cannot be assigned. Due to this, one can only define it via placement new and copy construction:
Def& operator = (const Def& obj)
{
this->~Def(); // destroy
new (this) Def(obj); // copy construct in place
}
But it is really unnecesary. Just use a pointer.
The following actually compiles and runs;
template <typename T>
class heap_ptr
{
public:
heap_ptr(T* p) : t(p) {}
heap_ptr(const heap_ptr&) = delete;
template<typename ... U> heap_ptr( U ... u )
{
t = new T(u...);
}
T* operator -> () { return t; }
// T& operator = (const T& o) { (*t)=o; return *t; }
operator T () { return *t; }
~heap_ptr() { delete t; }
private:
T* t;
};
struct A { int x,y; A(int x,int y):x(x),y(y){} };
void try_it()
{
heap_ptr<A> woop {8,11};
A a{5,3};
woop = a; // <- here
}
However, the marked assignment puts garbage in woop. Why does it compile, and
why do I get garbage in woop?
note: if I uncomment the assignment operator it works as expected, that's not the issue.
if I uncomment the assignment operator it works as expected, that's
not the issue
That's exactly the issue. Copy-assignment operator generated by default will copy the pointer, not the object behind it. And then the temporary object heap_ptr(a) is destroyed, deleting the data pointed to.
The problem is caused by the fact that by not implementing your operator= you are relying on the default one which will copy the pointer of the temporary object and then destroy it, leaving you with an invalid pointer once the copy is finished.
This is an instance of the set of problems that derive by not following the rule of three (five in C++11). When you can, you should follow the rule of zero, though.
You are trying to reimplement a smart pointer. Just use std::unique_ptr and it won't compile, like you expect:
struct A { int x,y; A(int x,int y):x(x),y(y){} };
void try_it()
{
std::unique_ptr<A> woop{ new A(8, 11) };
A a{5,3};
woop = a;
}
Live demo