virtual destructor

Pages: 12
I have created a hierarchy with a common base... but I'm having trouble when I create an object of my most derived class...I think it doesn't know which destructor to use....the programm doesn't make sense it's just an example to learn....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 #include <iostream>
#include <string>
using namespace std;

class Storable{
public:
	Storable(const string& s):file_name(s){};
	virtual void read() = 0;
	virtual void write() = 0;
	virtual ~Storable() = 0;
	
protected:
	string file_name;
};

class Transmitter: public virtual Storable{
public:
	void read(){
		
	}
	void write(){
		
	}
	~Transmitter(){
	}
	Transmitter(const string& s):Storable(s){};
};

class Receiver: public virtual Storable{
public:
	void read(){
		
	}
	void write(){
		
	}
	~Receiver(){
	}
	Receiver(const string& s):Storable(s){};
};

class Radio: public Transmitter, public Receiver{
public:
	void read(){
		
	}
	void write(){
		
	}
	~Radio(){
	}
	Radio(const string& s):Storable(s),Transmitter(s),Receiver(s){};
};

int main(){
	string a{"Hello"};
	Radio test(a);
	
	
} 





You need to provide a definition for the Storable destructor - an instance of a derived class calls the base class constructor when it is created & base class destructor at the end of its lifetime.
Norm's correct - destructors can't be pure virtual (they can't = 0).

Further, there's a map (the vtable) of what destructors to call, and the sequence, so that is not an issue.
destructors can be pure virtual. It's also perfectly legal to provide a definition for a pure virtual function - in the case of a pure virtual destructor, it's a necessity (because the base class destructor will always get called). The pure virtual just means that derived classes must also provide a definition of a destructor.

OP's code can be made to compile by doing:

1
2
3
4
5
6
7
8
9
10
11
12
class Storable{
public:
	Storable(const string& s):file_name(s){};
	virtual void read() = 0;
	virtual void write() = 0;
	virtual ~Storable() = 0;
	
protected:
	string file_name;
};

Storable::~Storable() {}
Last edited on
True, I realized about that, they must have a definition and all of them are called when the object finish its lifetime....but I was wondering one thing now..

I want to create a pure virtual interface( what I understand for that it's without data members) and manage to call function member of derived classes without having ambiguity but I'm failing at that....I have done this....the another one was fixed, thanks!!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

// pointers to base class
#include <iostream>
#include <string>
using namespace std;

class Storable{
public:
	
	virtual void read() = 0;
	virtual void write() = 0;
	virtual ~Storable() {
		
	}
};

class Transmitter: public Storable{
public:
	void read(){
		
	}
	void write(){
		
	}
	~Transmitter(){
	}
	Transmitter(int a):T(a){};
protected:
	int T;
};

class Receiver: public Storable{
public:
	void read(){
		
	}
	void write(){
		
	}
	~Receiver(){
	}
	Receiver(int a):R(a){};
protected:
	int R;
};

class Radio: public Transmitter, public Receiver{
public:
	void read(){
		
	}
	void write(){
		Transmitter::write();
		Receiver::write();
	}
	~Radio(){
	}
	Radio(int a,int b):Transmitter(a),Receiver(b){};
};

int main(){
Radio test(1,2);
Storable* ptr;
ptr = &test;

}


I didn't say that I don't want to use virtual base class...
It is safe to use pure interface class as virtual base, as it does not have any data which needs initializing.

So, you can use
1
2
class Transmitter: public virtual Storable{
class Receiver: public virtual Storable{
Norm, (and Winsu), that's sortof what I meant to say (stick with me Winsu, I have a question and a point - you need virtual inheritance!)...here's the rub:

The "pure virtual destructor" idiom is really an odd beast. It's not really pure virtual. It's a syntactic notion that does nearly nothing regarding the base class. A destructor must have a body. In 20+ years of C++ work, I've never encountered a pure virtual destructor in production code. Certainly virtual destructors - they may be required - but no pure virtual destructors. It's a solution searching for a problem, really.

Even the idiom of pure virtual functions which have definitions as some kind of default are hardly ever seen in practice. I think I noticed it in one 3D engine from 2002. Usually, pure virtual functions have no body, and derived classes are this invalid unless they provide an override. That's the concept of an enforced interface; even if you intend to do nothing, a pure virtual with no body forces an empty function to make that clear.

That's also motivation behind the purpose of the C++14/C++11 keyword, override. It informs the compiler that a derived class function intends to override a base class, which seems odd to elder C++ programmers, until we recall the mistakes that can cause without override.

Any slight difference in the declaration misses the match. If the (optionally pure) virtual function in the base has any minor difference in declaration from that of the derived, it isn't actually an override (in the derived class) and the compiler can be completely silent of that fact, leaving you to runtime discovery with a debugger.

Unless the derived class uses the keyword override. At that point the compiler knows to inform you if what you said is an override doesn't actually match a virtual function in base classes. I've lost many hours tracing down that exact problem over the years, and the override keyword helps static (compile time) checking that a virtual override does in fact match a base class virtual function.

Winsu:

What's the ambiguity you notice? In multiple inheritance, it can be unclear sometimes, what to expect. So, are you trying to chain through all parental versions of a function that's not working?

In your layout, did you notice you may have TWO Storables?

I've tried 4 compilers, and all complain about:

1
2
Storable* ptr;
ptr = &test;



Because the compiler isn't sure which of the TWO storables you mean.

Try:

 
class Receiver: virtual public Storable{


Virtual inheritance.

That's the solution to that puzzle...a common base from multiple parents creates TWO roots.

They're turned into a diamond shaped heritage graph by virtual inhertiance.

Do that on both receiver AND transmitter.






I know about the vtable...I know what is that, but is there any possiblitty of have access to that??, it could be ideal to have something like "graphic" to track the path a destructor follows to finish its lifetime
Yes, I knew that I had a double base, I mean creating a instance of the most derived class it will contain two Storable objects....but I though that I was legal to do it...that the compiled didn't launch an error for that...and then what I was trying to plan was to call all the write function......well, it apparently is not possible.....without using l public virtua, and maybe what I was trying to do didn't make sense.....it was just to learn..

it could be ideal to have something like "graphic" to track the path a destructor follows to finish its lifetime
You can easily figure out this yourself.

By standard, destruction works as follows:
First, a destructor body is executed.
Second, all class own members are destroyed in backward order of their creation (declaration). This might lead to more destructor calls.
Third, all non-virtual base classes are destroyed in backward order of construction. This might lead to more destructor calls.
----
Last: all virtual base classes are destroyed.
https://msdn.microsoft.com/en-us/library/6t4fe76c.aspx

Changes could be made by the optimiser, but you would not be able to see them, because they follows the as-if rule.

EDIT:
I mean creating a instance of the most derived class it will contain two Storable objects....but I though that I was legal to do it...that the compiled didn't launch an error for that...and then what I was trying to plan was to call all the write function
As CrashMeister said, there are 2 Storable objects. Which one you want to point to?

You can get rid of error by specifying which one is to point to: ptr = &(Receiver&)test;

Here it would point to Receiver instance of Storable
Last edited on
Maybe here, the point to see is why I would use a virtual pure virtual function or a normal virtual function. I see it like this...when you create a virtual pure function the must be override at any derived class otherwise classes that not override will be abstract classes, unable to instance objects, with a virtual function it doesn't happen. Maybe that was set to oblige or suggest programmers use a virtual pure function when it will be an interface shared by every derived class.

But that logic doesn't affect to constructors and destructors since they are by default or not. Apparently it is possible to createa pure virtual destructor with body( I always thought that pure virtual function didn't have definition). There is a fact that base destructor always get called...but if it belongs to a pure virtual interface there won't be any resource to release..so it doesn't have a body or at least the body would be superfluous for this matter...

I have read many times that if there is virtuality between classes ( the key word virtual is used) or there is pure virtual functions the destructor must be virtual( at least). And I think I agree with that , but I have understand the reason even being perfectly legal to declare a destructor pure virtual....

The destructor always are at any classs(default or not), and the base destructor always is called, if there is no body in the base is becasue there are no resources to release so, or there are not resources or the must be in other class to release , so other destructor will assume that duty...I think in the case of destructor pure and pure virtual is the same....

or at least I can see just difference grammatically, nothing else....is that all right???....and sorry if my explanation wasn't so well I' m still getting used with the terminology...

You're approaching the right track.

Here's the reason for virtual destructors (pure or not).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A
{
  ~A();
};

class B : public A
{
 std::vector<int>  integers;

 ~B();
};

int main()
{
 A * a = new B;

 // some stuff done with a

 delete a;

 return 0;
}



B has some dynamic storage. The vector class will allocate memory when it's used (just assume B has functions that do things with integers).

Now, we "know" the object as a "A" type via a pointer. We don't know if it's a B. It could be any other type using A as a base. So, we hold it as an A. Never mind the fact this trivial example makes it obvious we created a new B - that fact can be completely invisible in a complex program.

The point is that B MUST be correctly destroyed. If not, integers, a vector, will not be destroyed. It's memory would be left in limbo (a memory leak) if B's destructor is not called.

It's important to note that even if B doesn't have a destructor written, the default destructor created by the compiler would still be able to correctly destroy integers.

However, since we knew the object as an A type, and deleted it as an A type, B's destructor will not be called simply because the destructor was not declared virtual in class A.

The problem is corrected simply by declaring the destructor of class A virtual (pure or not), indicating to the compiler it has to figure out the entire chain for us when deleting this object via a pointer in class A.

Now, here's the opposite view of that problem. Let's say we did NOT use a type A pointer. Let's say we did:

1
2
3
4
5
6
7
8
9
10
11
12

int main()
{
 B * b = new B;

 // do stuff with b

 delete b;
 
 return 0;
}


In this case we know the object is a B. The type of the pointer is a B. Technically, for decades, we've been told that what happens in the destructor sequence call is undefined for this class design when A is not declared to have a virtual destructor.

The practical result, however, is that in all that time, compilers have consistently delete this object correctly. This is because, even though the standard does not define how this SHOULD happen, it just happens to work out that the compiler realizes this is a B, and calls the destructor sequence correctly.

It is undefined by the standard, and is therefore not a RELIABLE tactic. The compiler is free to choose to decide otherwise (it would still be a compliant compiler if it just called the A destructor).

The fact that compilers happen to do this correctly for the last 20 years has been used in production code, but is considered a risky mistake.

One reason is that there is no guarantee that, in the future, a programmer won't expand the program with a class C deriving from B, and change the "new B" to a "new C", leaving everything else the same.

Doing that would most DEFINITELY create the same memory leak scenario as the first example.

One key point about this:

The need for a virtual destructor is not explicitly associated with the use of any virtual functions. There can be a need to use a virtual destructor even though you have no other virtual functions in the design. The reason is the potential for casting, and holding objects via pointer to the base class. That is, while the use of a virtual function other than the destructor is an INDICATOR that the class will be used via pointer of a base class, using virtual functions as polymorphism, the primary reason for virtual destructors has to do with the fact you are deleting an object via a pointer from a base type (deleting A when the object is created as a B). THAT is the point of the virtual destructor, independent of whether or not there is any virtual function involved.


...and, as to terminology, we use the phrases "virtual function" and "pure virtual function", but not "virtual pure virtual".

Last edited on
In this case we know the object is a B. The type of the pointer is a B. Technically, for decades, we've been told that what happens in the destructor sequence call is undefined for this class design when A is not declared to have a virtual destructor.

This is because, even though the standard does not define how this SHOULD happen, it just happens to work out that the compiler realizes this is a B, and calls the destructor sequence correctly.
Standard does define what should happen. Type of pointer is B*, so it deletes the object as if it was B. It is safe and well-defined procedure. non-virtual destructors are dangerous only when you delete object through pointer to base class. (If that pointer would actually point to class C, derived from B, that is another story)

In most implementations std::vectors inherits from some base class without virtual destructor, and it is still safe to create and destroy vector object dynamically.

It is undefined by the standard, and is therefore not a RELIABLE tactic. The compiler is free to choose to decide otherwise (it would still be a compliant compiler if it just called the A destructor).
Your example, as it is, is well-defined and is reliable. If you created an object , it is always safe to destroy it through pointer to same type.
Last edited on
MiiNiPaa, I've been a C++ developer since 1987, and for decades....DECADES...it was NOT defined behavior.

That is a recent development, relatively.
common mistake , all your derived destructor must be virtual to respect the virtual hierarchy destructor call (which is a method by the way ) .
for decades....DECADES...it was NOT defined behavior.
It was defined starting from very first C++ standard (1998 one) I would not say, that 11 years are decades.

It is simple. If type of pointer is B* then B destructor is called. If no dynamic dispatch takes place, B::~B() destructor does its thing which includes calling A::~A() destructor. It is imperative, you cannot invoke B destructor without also calling A one.

A quote from C++98 standard:
A destructor for class X calls the destructors for X’s direct members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes


An inheritance without declaring virtual destructor is quite common. When you know that nobody will delete your object through base class pointer (for example when you inheriting privately), virtual destructor is not needed. This is the reason why destructor is not virtual by default: it is not always needed.
Last edited on
Thanks to everyone for his support, I'm learning a lot, but the more a read the more I doubt but at least with new things....that I think is good....

Miinipaa I have understood the mining of this ptr = &(Receiver&)test; it ends with the ambiguity by saying what path ans what set of Storable is going to take to call member functions, since in the declaration of Radio::test it has two set of Storable....that's fine...but the grammar is complex....operator & on the left means for me address of X and operator & on the right means a declaration of a reference of type X...so from &(Receiver&)test I can read so so address of a reference of type Receiver holding a Radio object....is it correct?





CrashMeister

It's important to note that even if B doesn't have a destructor written, the default destructor created by the compiler would still be able to correctly destroy integers.


I know that destructor deleted data members, but I always though that it didn't release memory allocated in the free store so it must be deleted explicitly by a delete . But you are speaking about memory leaks, it implies that something has allocated memory in the free store....then I can think that vector allocate memory in the free store and as a vector is a member data of class b as soon as its default destructor destroys its data member vector's destructor( i think it will get called) will release the memory allocated.....
ehhehehee, vector allocated memory dynamically...you have said at the beginning of your post...I leave the post because now I have other questions kind of related...is there any hierarchy relation between class B and std::vector...because when B's destructor get call it delete vector and then destructor's vector gets called so it's like ..
1
2
3
4
5
6
7
8
9
10
11
12

class vector{

~vector (){};

}

class B: public vector{
~B (){};
}

Pages: 12