Implementing a virtual destructor in C++
Asked Answered
B

3

6

I'm starting to learn c++ but I'm stuck in the destructor. We need to implement a vector and this is what I have so far.

#include<string.h>
#include<cassert>
#include<iostream>

using namespace std;
template<class T>
class Vector {
    template<class U> friend ostream& operator<<(ostream&, const Vector<U>&);
private:
    T* data;
    unsigned len;
    unsigned capacity;
public:
    Vector(unsigned = 10);
    Vector(const Vector<T>&);
    virtual ~Vector(void);
    Vector<T>& operator =(const Vector<T>&);
    bool operator==(const Vector<T>&);
    T& operator[](unsigned);
};

//PROBLEM! 
template <class T>
~ Vector() {
    delete data;

}

template<class T>
Vector<T>::Vector(unsigned int _capacity)
{
    capacity = _capacity;
    len = _capacity;
    data = new T[_capacity];
}

template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    len = v.len;
    capacity = v.capacity;
    data = new T[len];
    for (unsigned int i = 0; i < len; i++)
        data[i] = v.data[i];
}



template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
    delete[ ] data;
    len = v.len;
    capacity = v.capacity;
    data = new T [len];
    for (unsigned int i = 0; i < len; i++)
        data[i] = v.data[i];
    return *this;
}

template<class T>
bool Vector<T>::operator == (const Vector<T> & v)
{
    bool check = true;
    check &= (len == v.len);
    if (!check) return false;
    check &= (capacity == v.capacity);
    if (!check) return false;
    for (unsigned int i = 0; i < len; i++) {
        check &= (data[i] == v.data[i]);
        if (!check) return false;

    }
    return true;
}

template<class T>
T& Vector<T>::operator[](unsigned int index)
{
    return data[index];
}

The interface is given and I need to implement it. But this is so different from C and Java, that I'm a bit lost.


In the second exercise we need to implement something like this using a) the previous Vector implementation as derived class and b) the Vector as composition class, so maybe we will use the virtual destructor in one of the approaches?

void testAssociativeArray() { 
AssociativeArray<String, int> table;
 table["abc"] = 15;
 table["jkl"] = 12;
 table["xyz"] = 85;
 assert(table["jkl"], 12);
 }

template<class P, class Q>
class Pair {
P p;
Q q; public:
      Pair(const P& _p = P(), const Q& _q = Q()): p(_p), q(_q) {}
      P& objectP() {return p;}
      Q& objectQ() {return q;}
};
Behistun answered 26/10, 2012 at 12:2 Comment(1)
Just a note: you can improve your operator= by getting the argument passed in by value and using the "copy-and-swap" idiom (#3280043)Negotiation
N
10

First off, why do you think that the destructor should be virtual? Are you using polymorphism?

Second, you are using delete incorrectly for your array.

Since you used:

data = new T[length];

You must use the array syntax:

delete [] data;

Third, you need to put the namespace in front of all of your class function definitions:

template <class T>
Vector<T>::~Vector()
{
    delete [] data;
}

And just for your information, you declare the destructor like so...

virtual ~Vector(void);

As I mentioned, virtual is unnecessary unless you are using this class as a base or derived class in a polymorphic manner. For more information on when you need to use virtual destructors, look at the answer to this question.

In addition, the void in the parameters is also unnecessary. This used to be required in old C standard, but it is not in C++.

You should be able to declare it like so:

~Vector();

If you define AssociativeArray<P,Q> with a has-a relationship to Vector<T>, then you can simply make the class contain a Vector<Pair<P,Q> >. Declaring virtual methods in this case, are not needed, but can still be used--with some extra overhead.

If you define AssociativeArray<P,Q> with a is-a relationship to Vector<Pair<P,Q> >, then you should define some virtual methods in Vector<T>, including a virtual destructor.

The use of virtual methods only matters when using objects polymorphically through pointers and references. See this page.

AssociativeArray<String,Int>* myDerivedMap = new AssociativeArray<String,Int>();
delete myDerivedMap; //NO virtual methods necessary here. using a pointer to derived class

Vector<Pair<String,Int> >* myBaseMap = new AssociativeArray<String,Int>();
delete myBaseMap; //virtual methods ARE necessary here. using a pointer to base class
Needlepoint answered 26/10, 2012 at 12:6 Comment(11)
Could you please go more in depth with the virtual/non-virtual part?Douville
I linked to another question that explains that in a pretty straight-forward manner.Needlepoint
thank you. Indeed, I was more concerned about design, i.e. why shouldn't he allow one to safely inherit from his Vector. Anyway, I found good points here: #1647798Douville
Making the destructor virtual in a derived class does not make the base destructor virtual, so destructing a derived class via a base-class pointer will still not invoke the derived destructor. So, for any class that has the slightest chance to get inherited, it in general makes sense to declare the destructor virtual.Draggle
Also, if the base class has a virtual destructor, it's unnecessary to declare the destructor in derived classes as virtual. That virtuality is inherited automatically.Draggle
@Behistun Could use more information. You say the AssociativeArray<P,Q> class is supposed to use the Vector<T> class. Do you mean that it is supposed to derive from it? Or it is supposed to contain A vector<T>. It seems like you could implement the class by having AssociativeArray<P,Q> contain a Vector<Pair<P,Q> >.Needlepoint
Actually I need to write two versions. An has-it (composition) and a is-it (inherence) and i'm all lost.Behistun
@TilmanVogel - "for any class that has the slightest chance to get inherited": no, for any class that is designed to be derived from. Don't add overhead to support possible misuse.Uro
@PeteBecker Hm, I would maybe put it like that: Make the destructor virtual unless your class is designed not to be inherited. One reason for that could be performance considerations when you are creating and destructing lots of instances in an inner loop or such.Draggle
@Douville Sure, virtuality is inherited not only for the destructor.Draggle
@TilmanVogel - "designed to be derived from" vs. (not) "designed not to be inherited from" -- distinction without a difference. Unless you believe that some classes aren't designed.Uro
F
3
template<class T> 
Vector<T>::~Vector()
{
    delete [] data;
}

Note that you must use delete [] and not delete

Familial answered 26/10, 2012 at 12:4 Comment(0)
A
1

Should be

template <class T>
Vector<T>::~Vector() {
    delete[] data;
}
Alicia answered 26/10, 2012 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.