C++ copy constructor clear up on vector<Base*> of Derived* - c++

I have a class which uses a class of base pointers to derived objects, so I need to have my own desructor for deleting the vector's elements and custom copy and assignment funcitons. I'm not entirely sure about the preferred way of implementing a structure like below and writing the right copy and assignment constructors and destructors for it. May I ask you to guide me? I've read a lot and searched but I'm still not sure.
class Base
{
public:
Base();
~virtual Base();
int a;
int type; // Derived1 or Derived2
std::string b;
...
}
class Derived1 : public Base
{
public:
Derived1();
Derived1(const Base* a);
virtual ~Derived1();
}
class Derived1
{
Derived1::Derived1(const Base *a) : Base(a)
{
}
}
class Derived2 : public Base
{
public:
Derived2();
Derived2(const Base* a);
virtual ~Derived2();
std::string d1;
std::string d2;
int d3;
}
class Derived2
{
Derived2::Derived2(const Base *a) : Base(a) {
this->d1 = ((Derived2*)a)->d1;
this->d2 = ((Derived2*)a)->d2;
this->d3 = ((Derived2*)a)->d3;
}
}
class A
{
public:
A();
~A();
A(const A& a);
A& operator = (const A& a);
std::string someString;
std::vector<Base*> vect;
}
A::~A() {
std::vector<Base*>::iterator it = vect.begin();
while (it != vect.end()) {
delete (*it);
it++;
}
A::A(const A &a)
{
someString = a.someString;
for(std::size_t i = 0; i < a.vect.size(); ++i {
someString = a.someString;
Base* base = a.vect.at(i);
if(base->type == base::TypeD1) {
base = new Derived1( a.vect.at(i) );
vect.push_back( base );
}
else {
base = new Derived2( a.vect.at(i) );
vect.push_back( base );
}
}
}

Your loop in the destructor is fine in practice, and is the
usual solution. Formally, it is undefined behavior, since you
are leaving objects in the vector (pointers to deleted objects)
which aren't copiable, but in practice: the vector won't copy
them unless you resize it to something bigger, or insert or
erase on it. If you really want to avoid the undefined
behavior:
for ( auto current = vect.begin(); current != vect.end(); ++ current ) {
Base* tmp = *it;
*it = nullptr;
delete tmp;
}
But this is one case where I probably wouldn't bother (and
I tend to be more sensitive to undefined behavior than most).

First, do you actually need to copy and assign objects of type A? If no, the simple solution is:
class A
{
public:
A();
~A();
A(const A&) = delete;
A(A&&) = default;
A& operator=(const A&) = delete;
A& operator=(A&&) = default;
// ...
};
If yes, then you want some polymorphic way of copying the elements of the vector. (Any time at all you have if (b->type == Base::TypeD1) { do_this(); } else { do_that(); }, stop and think if it would make sense to add a virtual function for do_this/do_that. The else-if-heimer's way doesn't allow for future new derived classes; the virtual way does.)
class Base
{
public:
// ...
virtual Base* clone() const = 0;
// ...
};
class Derived1 : public Base
{
public:
virtual Derived1* clone() const;
};
Derived1* Derived1::clone() const {
return new Derived1(*this);
}
The copy assignment operator of A will need to destroy the old contents of the lhs, just like the destructor, and then copy the new contents over, just like the copy constructor. So let's put those two operations in private functions:
class A
{
public:
A();
~A();
A(const A&);
A(A&&) = default;
A& operator=(const A&);
A& operator=(A&&) = default;
// ...
private:
void destroy_contents();
void copy_from(const std::vector<Base*>& v);
};
void A::destroy_contents() {
std::vector<Base*>::iterator it = vect.begin();
while (it != vect.end()) {
delete (*it);
++it;
}
vect.clear();
}
void A::copy_from(const std::vector<Base*>& v) {
std::vector<Base*>::const_iterator it = v.begin();
while (it != v.end()) {
vect.push_back((*v)->clone());
++it;
}
}
A::~A() { destroy_contents(); }
A::A(const A& a) :
someString(a.someString),
vect()
{
copy_from(a.vect);
}
A& A::operator=(const A& a) {
if (this != &a) {
someString = a.someString;
destroy_contents();
copy_from(a.vect);
}
return *this;
}

If you have a vector of any pointer type, the vector does not know anything about the type behind the pointer, nor does it care: The vector operates on the pointers only, copying them to a new location if need be, but never even touching the objects themselves. As such, it is your responsibility to destroy the objects themselves.
As James Kanze points out, there is a slight danger of undefined behavior when handling invalidated pointers. However, since the vector is not used in any way after deleting the objects it holds, no undefined behavior is invoked in the code you've given (the vector won't need to reallocate its memory so it won't need to assign invalidated pointers, and destruction of pointers is a noop). As such, your destructor of class A is perfectly fine.
The copy constructor of class A, however, is unnecessarily complicated and an ample source of errors (it needs to be updated whenever a new derived class is defined!). The best way to do this is to use a clone() function:
class Base {
public:
//...
virtual Base* clone() const = 0; //Returns a new copy of the object. Pure virtual if the Base class is abstract.
};
class Derived1 : public Base {
public:
Derived1(const Derived& a);
virtual Derived* clone() const {
return new Derived1(*this);
}
};
If you make clone() pure virtual in the base class, you have the guarantee that your compiler will complain if you forget to implement it in any derived class. With that, the copy constructor of class A is next to trivial:
A::A(const A &a) {
someString = a.someString;
for(std::size_t i = 0; i < a.vect.size(); ++i {
vect.push_back(a.vect[i]->clone());
}
}

Related

How to use an abstract class rvalue reference member?

I have an abstract class Base and derived class Derived:
class Base
{
public:
Base(int n) :_n(n) { _arr = new int[n]; }
virtual ~Base() { delete[] _arr; }
Base(Base&& other) { _n = other._n; _arr = other._arr; other._arr = nullptr; other._n = 0; }
virtual void func() = 0;
private:
int _n;
int* _arr;
};
class Derived : public Base
{
public:
Derived(int m, int n) : Base(n), _m(m) { _arr = new int[m]; }
~Derived() { delete[] _arr; }
Derived(Derived&& other) : Base(std::move(other)) { _m = other._m; _arr = other._arr; other._arr = nullptr; other._m = 0; }
void func() override { cout << "func"; }
private:
int _m;
int* _arr;
};
Then I have a class Bag which contains a rvalue reference of Base:
class Bag
{
public:
Bag(Base&& b) : _b(std::move(b)) {}
void func() { _b.func(); }
private:
Base&& _b;
};
int main()
{
Bag bag(Derived(1, 1));
bag.func();
}
I use a rvalue reference member _b because I just want to take a temporary object in Bag's constructor. However, after bag.func() I got an error: abort() is called. It seems that bag._b's type changes from Derived to Base after the temporary object Derived(1,1) is destructed.
bag.func() works after I delete virtual ~Base() { delete[] _arr; }. Why? If I need Base's destructor, how can I change my code?
Base&& _b is a reference to the temporary object, when that temporary is destroyed at the end of the line Bag bag(Derived(1, 1)); the reference becomes a dangling reference and any use of the reference is undefined behaviour.
You could change _b to a value instead but that would slice your object.
If you want to store a polymorphic object the only real option is to use a pointer (ideally a smart one). For example:
class Bag
{
public:
Bag(std::unique_ptr<Base>&& b) : _b(std::move(b)) {}
void func() { _b->func(); }
private:
std::unique_ptr<Base> _b;
};
int main()
{
Bag bag(std::make_unique<Derived>(1, 1));
bag.func();
}
Note that you should also ensure that Base and Derived follow the rule of five otherwise you may encounter issues when you assign objects.

Using smart pointers to keep track of data members that may be deleted

I have two classes A and B. I compute a B deterministically from an A. For each A, I want to keep track of the B with my_B for as long as it exists. Once the B is destructed, I want the my_B to be changed to something like nullptr.
class A{
// stuff
public:
B ComputeB(){
if (my_B is null){
B result = B(A);
my_B = B; // some kind of reference
return B(A);
}
else {
return my_B;
}
}
~A(){ /* Do I need a destructor? */ }
private:
WhatTypeHere my_B;
}
When B is destructed, what will cause my_B to refer to nullptr (or the equivalent for WhatTypeHere)?
Using shared_ptr and weak_ptr
In order to keep your B object alive in A as long as it is still in use, you should have a data member in A of type std::weak_ptr<B> this will allow to access the created B object, as long as it is alive.
The return value from computeB would be std::shared_ptr<B> that would be either acquired from the std::weak_ptr<B> member or created, if the latter is holding nullptr.
Thread safety
The decision whether to create or acquire the existing B shall be thread-safe. For this you shall try to fetch the actual B held by the weak_ptr by using the lock() method, then only if the return value is nullptr create a new one.
The code would look like this:
class A {
// stuff
public:
std::shared_ptr<B> ComputeB() {
std::shared_ptr<B> shared_b = my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(*this);
my_B = shared_b;
}
return shared_b;
}
// no need for a destructor, unless "stuff" needs one
// ~A(){}
private:
std::weak_ptr<B> my_B;
};
Copy and assignment
The behavior of above class in copying and assignment is problematic, as the default copy constructor and default assignment operator would perform member-wise copy/assignment, which may result with two different A's holding a weak_ptr to the same B. Most probably this is not what you want, especially not if A is mutable (i.e. can change its inner values).
To present suggested code for copy and assignment, let's assume A holds an int member. The code would then look like this:
class A {
int i;
public:
A(int i1): i(i1) {}
void set(int i1) { i = i1; }
std::shared_ptr<B> ComputeB() {
std::shared_ptr<B> shared_b = my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(*this);
my_B = shared_b;
}
return shared_b;
}
A(const A& a): i(a.i) {}
A& operator=(const A& a) { i = a.i; return *this; }
~A() {}
private:
std::weak_ptr<B> my_B;
};
Preserving constness
In the code above, the call to ComputeB() cannot be done on a const A object. If we want to support that we need to have a const version of this function. For matter of semantics I prefer renaming this method (both the const and non-const versions) to getB.
To present suggested code that adds the option for calling getB on a const A object we need to present also an example of class B which is able to hold either a const or non-const reference to A. The code would then look like this:
class A {
int i;
// to prevent code duplication for the const and non-const versions
template<typename AType>
static auto getB(AType&& a) {
std::shared_ptr<B> shared_b = a.my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B>(std::forward<AType>(a));
a.my_B = shared_b;
}
return shared_b;
}
public:
A(int i1): i(i1) {}
void set(int i1) { i = i1; }
std::shared_ptr<B> getB() {
return getB(*this);
}
std::shared_ptr<const B> getB() const {
return getB(*this);
}
A(const A& a): i(a.i) {}
A& operator=(const A& a) { i = a.i; return *this; }
~A() {}
private:
mutable std::weak_ptr<B> my_B;
};
And for B:
class B {
union Owner {
A* const ptr;
const A* const const_ptr;
Owner(A& a): ptr(&a) {}
Owner(const A& a): const_ptr(&a) {}
} owner;
public:
B(A& a): owner(a) {}
B(const A& a): owner(a) {}
const A& getOwner() const {
return *owner.const_ptr;
}
A& getOwner() {
return *owner.ptr;
}
};
On the use of union to manage const and non-const versions of the same pointer, see:
Union of const/non-const Object Pointers
Is const-casting via a union undefined behaviour?
Working example: http://coliru.stacked-crooked.com/a/f696dfcf85890977
Private creation token
The code above allows anybody to create objects of B which may lead to undesired possibilities, like creating a non-const B object via the constructor that gets const A& a, resulting with a potential casting from const to non-const when calling getOwner().
A good solution might be to block the creation of B and allow it only from class A. Since the creation is done via make_shared putting B's constructors in the private section of B with a friend declaration for A wouldn't help, it is not A that is calling new B it is make_shared. So we go for a private token approach as in the following code:
class A {
int i;
// only authorized entities can create B
class B_PrivateCreationToken {};
friend class B;
template<typename AType>
static auto getB(AType&& a) {
std::shared_ptr<B> shared_b = a.my_B.lock();
if (!shared_b){
shared_b = std::make_shared<B> (
std::forward<AType>(a),
B_PrivateCreationToken{} );
a.my_B = shared_b;
}
return shared_b;
}
public:
// public part as in above version...
private:
mutable std::weak_ptr<B> my_B;
};
And for B:
class B {
union Owner {
A* const ptr;
const A* const const_ptr;
Owner(A& a): ptr(&a) {}
Owner(const A& a): const_ptr(&a) {}
} owner;
public:
B(A& a, A::B_PrivateCreationToken): owner(a) {}
B(const A& a, A::B_PrivateCreationToken): owner(a) {}
// getOwner methods as in above version...
};
Code: http://coliru.stacked-crooked.com/a/f656a3992d666e1e
You can return a std::shared_ptr from ComputeB(), and make my_B a std::weak_ptr. Something like this:
std::shared_ptr<B> ComputeB() {
if (my_B.expired()) {
auto result = std::make_shared<B>(*this);
my_B = result;
return result;
} else {
return std::shared_ptr<B>(my_B);
}
}
private:
std::weak_ptr<B> my_B;
The idea is that any caller of ComputeB becomes the partial owner of the B instance, which means that it will only be destroyed when all shared_ptrs to it are destroyed. The purpose of the weak_ptr is to point to the B instance without owning it, so the lifetime isn't tied to the A instance at all

deep copy and dynamic cast unique_ptr

Suppose I have a class like the following:
class A { virtual ~A(); ... }
class B : public A { ... }
class C : public A { ... }
I also have a vector of unique_ptr which is declared this way:
std::vector<std::unique_ptr<A>> vec;
Assume vec is populated with unique_ptr to objects of derived class. What should I do if I want a deep copy of any of the vector elements, either b or c, and let a base class unique_ptr pointing to it? Originally I was doing things like
std::unique_ptr<A> tmp = std::make_unique<A>(*b);
I don't think this is correct.
One possible solution is to declare a virtual cloning method in the base class and override it for each subclass:
class A {
virtual ~A() {}
virtual std::unique_ptr<A> clone() const = 0;
}
class B : public A {
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
Edit:
An usage example:
void f(const A& original) {
std::unique_ptr<A> copy = original.clone();
// Here, copy points to an instance of class B.
}

An error on delete a 2D array in class

I'm designing a class that's wrapper of a 2D array. Anything seems ok, except the destruction function. I don't know why it throws an access violent reading.
I have tried to detect what happened and I still can't solve this problem. I need your help. Thank you.
class ClassA
{
int Num;
public:
ClassA()
{}
~ClassA(void)
{}
};
class ClassB
{
ClassA* pA;
public:
ClassB()
{}
ClassB(ClassA obj)
{
pA = new ClassA[1];
pA[0] = obj;
}
~ClassB(void)
{
delete[] pA;
}
ClassB::ClassB(ClassB& src)
{
this->~ClassB();
pA = new ClassA[1];
pA[0] = src.pA[0];
}
ClassB& ClassB::operator =(const ClassB& src)
{
if (this == &src)
return *this;
this->~ClassB();
pA = new ClassA[1];
pA[0] = src.pA[0];
return (*this);
}
};
ClassB Test5()
{
ClassA A;
ClassB B( A );
return B;
}
void Test6()
{
ClassB B;
B = Test5();
}
The exception will be thrown after finishing of Test6 function.
Get out of the habit of explicitly calling destructors. Such an act does not reinitialise the object or its (non-static) members - it logically destroys the object so it cannot be used.
this->~ClassB() makes any attempt to access non-static members of the current object (i.e. *this) give undefined behaviour.
For example, in the copy constructor of ClassB
ClassB::ClassB(ClassB& src)
{
this->~ClassB();
pA = new ClassA[1];
pA[0] = src.pA[0];
}
the accesses of pA in the last two statements are (implicitly) this->pA. So they both have undefined behaviour.
There are circumstances where explicitly calling a destructor is appropriate. But assignment operators and copy constructors are generally not among those circumstances.

A strange access violation error

I have wrote a program that use inheritance, everything is OK but an error that I think shouldn't not be here naturally.
here is my program:
class A
{
protected:
int x;
public:
A(){};
~A(){};
A* operator=(const A* other)
{
this->x = other->x;
return this;
}
};
class B : public A
{
public:
B() : A(){};
~B(){};
};
class C
{
protected:
A *a;
public:
C(const A* other){ *(this->a) = other; };
};
int main()
{
B *b = new B();
C *c = new C(b);
return 0;
}
It produces an execution time error in the statement 'this->x = other->x;'.
What about this?
*(this->a) is undefined behavior, because this->a wasn't initialized - it's just a dangling pointer.
You can use a = new A; *a = other (the this-> in this case is redundant), but this isn't the proper C++ way - you should use RAII (look it up) - you wouldn't need destructors, assignment operators or copy constructors if you did.
Also, operator = typically returns *this by reference.
I like Luchian's answer but there is still some places to enhanced in your code, you may still encounter some undefined behavior in your code before you fix other potential bugs.
class A
{
protected:
int x;
public:
A():x(0){} // should always initialize x in constructor
virtual ~A()=0 {} // as A is a base case, ~A must be virtual
A& operator=(const A& other) // a reference will be more practical better?
{
this->x = other.x;
return *this;
}
};
If you don't want to make B object as well which means B is serviced as a base class, you should make its destructor virtual as well, even make it pure virtual.
class B : public A
{
public:
B() : A(){}
virtual ~B(){} =0
{ }
};
*(this->a) = other; is answered by Luchian already. In your code, if you want to keep a copy of pointer which points to A, you can simply initialize a_ptr in member initialize list
see below demo code:
class C
{
protected:
A *a_ptr;
public:
C(A* other):a_ptr(other){ }
};
Finally come to your main function, if b,c are only used in main function, when program finishes, system will claim dynamic allocated memory back, but if you use a,b in a loop, you need to delete them manually.