polymorphism

Hi everyone, I'm trying polymorphism for the first time. I'm getting this warning when compiling the codes:

warning: deleting object of polymorphic class type ‘particle’ which has non-virtual destructor might cause undefined behavior [-Wdelete-non-virtual-dtor]

Anyone who could explain to me what this warning is about? (thank you in advance)

Here the code:

main.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "particle.h"

int main () {

	particle* a { new particle ( 1. , 2. ) } ;
	electron* b { new electron () } ;
	particle* c { new electron () } ;
	particle* d { new proton () } ;
	

	a->print() ;
	b->print() ;
	c->print() ;
	d->print() ;

	delete a ;
	delete b ;
	delete c ;
	delete d ;

	return 0 ;
}


particle.h
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
70
  #pragma once 

//order of data members: charge , mass --> particle p ( q, m)
#include<iostream>

//************************************** General Particle

class particle {

 public:
									//constructors
 particle () ;
 particle ( double , double ) ;
 particle ( const particle& ) ;
 particle ( particle&& ) ;
 particle& operator = ( const particle& ) ;
 particle& operator = ( particle&& ) ;

 //destructor
 ~particle () { ; } ;

 
									//methods
 double getCharge () const { return m_charge ; } ;
 double getMass () const { return m_mass ; } ;
 virtual void print () const ;  

 protected:

 double m_charge , m_mass ;
 
};


//************************************** Electron
class electron : public particle {

 public:

 electron () : particle ( -1.60217653E-19 ,  9.1093826E-31 ) { } ;
 //destructor
 ~electron () { ; } ;
 
 void print () const override { std::cout << "Electron of charge: " 
 					   << m_charge << " and mass: " << m_mass << std::endl ;
 };
 
};

//mass: 9.1093826E-31
//charge: -1.60217653E-19


//************************************** Proton
class proton : public particle {

 public:

 proton () : particle ( 1.60217653E-19 ,  1.672622E-27 ) { } ;
 //destructor
 ~proton () { ; } ;
 
 void print () const override { std::cout << "Proton of charge: " 
 					   << m_charge << " and mass: " << m_mass << std::endl ;
 };
 
};

//mass: 1.6726219E-27 
//charge: 1.60217653E-19 


particle.cxx (I leave it just in case someone wanna compile the whole thing )
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
  #include "particle.h"

#include<iostream>

																		//constructor
particle::particle () : m_charge {} , m_mass {} { }

particle::particle ( double q , double m ) : m_charge { q } , m_mass { m } { }

//copy constructor
particle::particle ( const particle& P ) : particle ( P.m_charge , P.m_mass ) { }

//move constructor
particle::particle ( particle&& P ) : particle ( P.m_charge , P.m_mass ) { 
	P.m_charge = 0 ;
	P.m_mass = 0 ;
}

//copy initializer 
particle& particle::operator = ( const particle& P ) {
	m_charge = P.m_charge ;
	m_mass = P.m_mass ;

	return *this ;
}

//move initializer 
particle& particle::operator = ( particle&& P ) {
	m_charge = P.m_charge ;
	m_mass = P.m_mass ;

	P.m_charge = 0 ;
	P.m_mass = 0 ;

	return *this ;	
}

																	//methods
void particle::print () const {
	std::cout << "Particle of charge: " << m_charge << " and mass: " << m_mass << std::endl;
}
Destructor of your base class:

1
2
//destructor
 ~particle () { ; } ;


Is bad constructor because it's not virtual.

The reason why it must be virtual is because your base class has at least one virtual method.
This means destructor must be declared virtual because if derived class is deleted trough a pointer to base class then your base class would be not destructed, that is your derived object would be improperly destructed.

To fix this declare base class destructor virtual:

1
2
//destructor
virtual ~particle () { ; } ;


Only base class destructor needs to be declared virtual, destructors of all derived classes are then implicitly virtual as well.
1
2
3
4
5
6
class particle {

public:
  virtual ~particle () { ; } ;

};



PS. The implementation of your destructor has an empty statement. Why?
Lets reformat to stress the point:
1
2
3
4
5
6
7
8
9
10
class particle {

public:
  virtual ~particle () // member-specification for destructor
    { // definition (aka implementation) of the function
      ; // empty statement
    }
  ; //  A semicolon after a member function definition is optional.

};

It would be sufficient to write:
1
2
3
4
5
6
class particle {

public:
  virtual ~particle () {}

};

Since C++11 it has been possible to state that compiler generates the implementation for destructor:
1
2
3
4
5
6
class particle {

public:
  virtual ~particle () = default ; // semicolon is not optional

};

If you want to move the implementation to the source file:
1
2
3
4
5
6
7
8
9
class particle {

public:
  virtual ~particle () ; // declaration, no definition

};

// in source file:
particle::~particle () {} // definition 
Last edited on
Thanks guys! Now it's clear.

keskiverto I did implement an empty destructor because I thought that the compiler would have called the default constructor anyway ( since a particle is built whith standard double variables ).
But now I'm using the "= default" implementation. Thanks.
Hi,

It would be better IMO if you didn't use new and delete, it looks like a classic case of using new to obtain a pointer.

One could use a smart pointer (std::unique_ptr or std::shared_ptr) instead. Or simply use an ordinary pointer or reference.

If new is to be used, put the delete into the destructor.

The problem with new is if an exception is thrown, the destructor is never reached.


The particle class should have a pure virtual function, so one cannot instantiate it.

protected variables are nearly as bad as public ones, you are already initializing the base class members anyway, and you have get functions for them.
The problem with new is if an exception is thrown, the destructor is never reached.


You can use a nothrow version of new and test the return pointer (as you did in C with malloc/calloc).

@seeplus

You are right about that :+)

I guess in this case all the ctors could be noexcept seen as they only initialise a couple of doubles, unlikely to throw anyway. @Zhylian just be aware that if one uses noexcept, then alters the code later with something that could throw, and it does, then then std::terminate will be called.

I still maintain that the OP used new to obtain a pointer. A reference would be perfectly OK here.

@Zhylian
Actually one more thing: make use of the [[nodiscard]] attribute (C++17) with functions that return a value.

1
2
[[nodiscard]]
double getCharge () const { return m_charge ; }


This will make the compiler check that one does use the value returned, it's a good thing :+)
How can I use polymorphism with ordinary pointers or reference. As I know, polymorphism should be defined at run time; the only way I can do that is by using the keyword 'new'. ( but I'll check the documentation about it).

About smart pointers, I didn't know about them since now. I will defenetly do some research on them, thank you for the hint man, I really appreciate it.
Hi

To use an ordinary pointer, just use & to take the address of an object.

1
2
3
4
5
6
7
proton c;
proton* ptr = &c; //address operator &

ptr->print();

electron& b; // reference to an electron
b.print();      


The other thing to do is have a container of pointers, then use a range based for loop to iterate through them all, calling the same function for each and seeing how the behaviour changes with each type of object. This works with pointers: one can't have a container of references. There is a work around by using std::reference_wrapper.

Just want to reiterate why one shouldn't be able to create a general particle: general particles don't exist, one should only be able to create a concrete particle type. So that's why you need a pure virtual function in the particle class.

If you make the member variables private as I mentioned earlier, then you will need get functions for them, for use in your print function. Or one could investigate writing your own operator<< instead of the print function; still need to have variables private.
I did the class particle like this in order to create another class "Material Point", which is derived from two classes: particle and position ( described by 3 double ). For this reason I created a general class particle, in order to study the behavior of a general material point of mass m ( or charge q, or both ).
The idea is that now, my class Material Point iheritate the protected data members of particle and position.
It seemed to me a simple design to represent the object Material Point and do stuff with it( like gravitational/electric field evaluation at a certain point in space).

back to the problem, I used simple pointer and references in order to abilitate the polymorphism of c++ (it worked).

1
2
3
4
5
6
7

        //building a pointer of type particle pointing to an object of type proton
	proton prot ;
	particle * Try = &prot ;
	
        Try->print () ;


I still don't know why using the keyword new is worse. It's because of the memory space involved? (As I know, with the keyword new, I explicit the compiler to build an object at run time. In order to do that, the memory in which the variables are stored is the heap, which is slower than the stack of the RAM)
Tell me if I'm wrong or if I'm missing something important. I'm a physicist, so I'm not an expert on programming theory, but I'm interesteed about it!

Anyway, all this was just an experiment to use polymorphism. (I used it with more sense by building a class BaseFunction with a virtual method "evaluate ( double x )" that is implemented specifically for any specific funtion, like parabola, sin, cos, ecc. )
I can see this is related to your previous topic. Please don't create a new topic about the same subject, it's ultimately a time waster for those who reply: the same things are said in both topics.

Did you take on board MikeyBoy's last post in the other topic?

The idea is that now, my class Material Point iheritate the protected data members of particle and position.


As MikeyBoy pointed out you should use composition here, not inheritance. Further to that, there are 3 relationship types:

1. "IS A" A Toyota IS A car, implies public inheritance;
2. "HAS A" A car HAS wheels, implies composition, the Wheel type is a member of Car.
3. "USES A" A Car can tow a ("USES A") trailer. The Trailer type is an argument to a function that involves Car. int Car::ConnectTrailer(const Trailer& MyTrailer);

using new is bad because if an exception is thrown, the destructor is never called and memory is leaked.

The best thing to do is use an STL container like std::vector for example, it stores it's data on the heap and takes care of memory management for you. Doing memory management yourself is error prone.

Having protected data is bad because if one has to change anything it could break code. Keep data private, and have a getter function to retrieve it. The fact that you have a function now means that you can alter the internals of the function without breaking the code that calls that function. Note, lots of people think OOP is easy: there are related functions & data in a class. But that is not the case, there is quite a lot to learn to do OOP properly.

Storing data on the stack is fast, but limited in size, on my Linux system it is only 8MB. Storing on the heap is slower, because the memory allocation is a slow heavy function. But it is not so bad if one allocates all the memory at once. STL containers like std::vector have functions such as reserve() which will allocate in one step. So if one had 1 million items in a vector, that could all be allocated at once with a call to the reserve function.

As you have proved to yourself, ordinary pointers work with polymorphism, without using new. The runtime aspect of polymorphism works via a vtable (virtual table). As far as I understand it, it is a table of pointers which allows the compiler to check that a pointer or reference is a derived class of a base class, and ultimately allows the correct virtual function to be called.
Last edited on
Wow, I've tons to learn on OOP. You gave me many topics to study, I really appreciate it. (sorry about any repetitions on this topic). I'm starting reading Thinking in C++, hopefuly I will find good infos there.
Last edited on
I couldn't hope for better.
Topic archived. No new replies allowed.