473,396 Members | 1,755 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Polymorphism without virtual in C++

Hi,All
I am sure it's an old question. But I just find a interesting design
about this: Polymorphism without virtual function in a C++ class.
My solution is for some special case, trust me, very special.
1 single root class tree
2 the leaf(lowest level) classes are sealed which means we should not
inherite class from them.
3 PImpl idiom. There is only one data mumber in root class and there
is no any other data mumber in child class and virtual funtions.

In my solution, the destructor of root class is not virtual, but we
can use base class pointer to point derived class object.

My question is: is this design follow the C++ standard? I tested it in
VS2005. it's ok. How about GCC?I remember that this non-virtual
destructor behavor is undefine in C++ standard.

Here is a simple code example:

class base
{
public:
~base()
{
delete[] p;
};

protected:
int *p;

base():p(new int[10])
{
};

base(int *pp) : p(pp)
{
};
};

class base1 : public base
{
protected:
base1()
{
};
};

class my : public base1
{
public:
my ()
{
p = new int[10];
};
};

int _tmain(int argc, _TCHAR* argv[])
{

base1 *o = new my;

delete o;
return 0;
}
Aug 6 '08 #1
12 2498
feel wrote:
Hi,All
I am sure it's an old question. But I just find a interesting design
about this: Polymorphism without virtual function in a C++ class.
My question is, "why?"
My solution is for some special case, trust me, very special.
1 single root class tree
2 the leaf(lowest level) classes are sealed which means we should not
inherite class from them.
3 PImpl idiom. There is only one data mumber in root class and there
is no any other data mumber in child class and virtual funtions.

In my solution, the destructor of root class is not virtual, but we
can use base class pointer to point derived class object.

My question is: is this design follow the C++ standard? I tested it in
VS2005. it's ok. How about GCC?I remember that this non-virtual
destructor behavor is undefine in C++ standard.
Correct. It's undefined behaviour to delete the derived class object
through the base class pointer in the absence of a virtual destructor.
>
Here is a simple code example:

class base
{
public:
~base()
{
delete[] p;
};
Drop the semicolons after all the function bodies. They are superfluous
(although not an error).
>
protected:
int *p;

base():p(new int[10])
{
};

base(int *pp) : p(pp)
Need a comment here that 'base' takes ownership of the pointer passed to
it. Also, probably want to declare this c-tor "explicit".
{
};
};

class base1 : public base
{
protected:
base1()
{
};
};

class my : public base1
{
public:
my ()
{
p = new int[10];
Memory leak here.
};
};

int _tmain(int argc, _TCHAR* argv[])
There is no standard function '_tmain'. There is no standard type
'_TCHAR'. You must have forgotten to include the proper header[s].
Don't check your code with VC++ without turning off extensions.
{

base1 *o = new my;

delete o;
Ka-boom! Undefined behaviour.
return 0;
}
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Aug 6 '08 #2
Victor Bazarov <v.********@comAcast.netwrote in news:g7cmb5$6so$1
@news.datemas.de:
>{

base1 *o = new my;

delete o;

Ka-boom! Undefined behaviour.
Not necessarily Ka-boom, you might get nasal demons instead.

Seriously though, the problem is that if you add any member variables to
your derived class, Base' destructor won't know how to clean them up and
will probably only return a fraction of the memory to the heap. The rest
would be left dangling... holding onto file handles, memory, mutexes... IOW
nasal demons. In this very simple case, the undefined behavior may look
like its working, but it wouldn't take much before that changed and it did
something very hard to debug because, of course, the bug would show up
elsewhere as some random behavior.

joe
Aug 6 '08 #3
feel wrote:
I am sure it's an old question. But I just find a interesting design
about this: Polymorphism without virtual function in a C++ class.
I really can't see where the polymorphic part of your code is.

You *can* create some kind of polymorphism without using the keyword
'virtual', for example by using member function pointers (rather than
virtual functions), but that doesn't really make too much sense, as
putting member pointers in the class is only counter-productive and
doesn't achieve anything 'virtual' wouldn't.
3 PImpl idiom. There is only one data mumber in root class and there
is no any other data mumber in child class and virtual funtions.
So there's a pointer to dynamically allocated data in your base class.
Exactly which part of this is polymorphic?

(Personally I really can't understand what's all the fuss about the
Pimpl idiom. It only makes classes consume more memory and slower, which
can be especially counterproductive with small classes which have to be
instantiated frequently and in large amounts. The only situation where
there may be an advantage is if the class is large, it's copied around a
lot, and the Pimpl data is reference-counted or CoW'ed.)
In my solution, the destructor of root class is not virtual, but we
can use base class pointer to point derived class object.
The base class destructor can destroy the data in the base class, yes.
I still fail to see the polymorphic part.

The real problem, as presented by others, is that if you ever add
anything in a derived class that needs to be destroyed, deleting the
object through a base-class pointer will most probably not destroy the
data in the derived class.

(Technically speaking deleting through a base class pointer without a
virtual destructor is undefined behavior even if the derived class is
empty, but I suppose most compilers will do what you expect.)
class base
{
public:
~base()
{
delete[] p;
};

protected:
int *p;

base():p(new int[10])
{
};

base(int *pp) : p(pp)
{
};
};
You probably shortened the example for the sake of brevity, but a few
comments nevertheless:

- Allocating unmanaged memory in the initialization list of the
constructor is asking for trouble.

- If objects of this class are ever copied or assigned, problems will
happen.

- Deleting a pointer given to the constructor is dubious practice at
best. You can't know what it's pointing to.
class base1 : public base
{
protected:
base1()
{
};
};
I didn't quite understand what's the purpose of this class. It does
nothing 'base' wouldn't already do.
class my : public base1
{
public:
my ()
{
p = new int[10];
};
};
This is a good example of why member variables in the protected
section are as bad as in the public section. You are assigning something
to 'p' without any regard to what it might have. In this case it has
already memory allocated to it, which is leaked by your assignment.
Aug 7 '08 #4
My code is a bad example ~_^. But if you can check with AcGe classes
in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
design. The special case is:
1 for geometry class, esp for leaf classes, we donot want user to
extend it. if you want to add other kind of geometry type, please add
new class.The leaf of class tree is 'sealed' class.
2 Polymorphism in this design is the Impl pointer in root class. we
can implementation the real version class for every leaf class.
3 Except for leaf class, every class can not be initialize in the
stack.in my example code, you can not do this:
base1 b;

Why? because I donot want a big class in my header file. For geometry
classes,such as AcGeNurbCurve, there are many method we have to put
into interface. but we can split all these method into different
catalog: 3dentity, curve, spline curve. Then you will see a class
tree. There are small number methods in every node(classes in the
tree). And it's meaningfull for a geometry class library. we may do
something for all kind of curves,right? so if we use Pimpl idiom, you
can find Polymorphism.
Aug 7 '08 #5
On Aug 6, 7:40 pm, Joe Greer <jgr...@doubletake.comwrote:
Victor Bazarov <v.Abaza...@comAcast.netwrote in news:g7cmb5$6so$1
@news.datemas.de:
{
base1 *o = new my;
delete o;
Ka-boom! Undefined behaviour.
Not necessarily Ka-boom, you might get nasal demons instead.
Seriously though, the problem is that if you add any member
variables to your derived class, Base' destructor won't know
how to clean them up and will probably only return a fraction
of the memory to the heap.
The problem here is that it is undefined behavior, and that
anything can happen. I can't think of a reasonalble
implementation where only part of the memory is freed, but I can
certainly think of cases where it will corrupt the free space
arena, and cause the program to crash, either immediately, or
sometime later.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 7 '08 #6
On Aug 7, 1:59 am, Juha Nieminen <nos...@thanks.invalidwrote:
feel wrote:
(Personally I really can't understand what's all the fuss
about the Pimpl idiom. It only makes classes consume more
memory and slower, which can be especially counterproductive
with small classes which have to be instantiated frequently
and in large amounts. The only situation where there may be an
advantage is if the class is large, it's copied around a lot,
and the Pimpl data is reference-counted or CoW'ed.)
It reduces coupling, sometimes enormously.
In my solution, the destructor of root class is not virtual, but we
can use base class pointer to point derived class object.
The base class destructor can destroy the data in the base class, yes.
I still fail to see the polymorphic part.
The real problem, as presented by others, is that if you ever add
anything in a derived class that needs to be destroyed, deleting the
object through a base-class pointer will most probably not destroy the
data in the derived class.
The real problem is that even if he never adds anything, it is
undefined behavior.
(Technically speaking deleting through a base class pointer without a
virtual destructor is undefined behavior even if the derived class is
empty, but I suppose most compilers will do what you expect.)
Most don't. (I'd expect an immediate program crash, but with
most, it will work most of the time, only crashing in specific
cases, or much later.)

[...]
class base
{
public:
~base()
{
delete[] p;
};
protected:
int *p;
base():p(new int[10])
{
};
base(int *pp) : p(pp)
{
};
};
You probably shortened the example for the sake of brevity, but a few
comments nevertheless:
- Allocating unmanaged memory in the initialization list of the
constructor is asking for trouble.
Why? In this case, he's doing it in the approved fashion:
passing the pointer immediately to the base class or member.
- If objects of this class are ever copied or assigned, problems will
happen.
- Deleting a pointer given to the constructor is dubious practice at
best. You can't know what it's pointing to.
That depends on the contract. Is boost::shared_ptr dubious
practice? It certainly deletes a pointer given to the
constructor.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 7 '08 #7
On Aug 7, 3:51 am, feel <feel...@gmail.comwrote:
My code is a bad example ~_^. But if you can check with AcGe classes
in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
design. The special case is:
1 for geometry class, esp for leaf classes, we donot want user to
extend it. if you want to add other kind of geometry type, please add
new class.The leaf of class tree is 'sealed' class.
2 Polymorphism in this design is the Impl pointer in root class. we
can implementation the real version class for every leaf class.
3 Except for leaf class, every class can not be initialize in the
stack.in my example code, you can not do this:
base1 b;
Why? because I donot want a big class in my header file. For geometry
classes,such as AcGeNurbCurve, there are many method we have to put
into interface. but we can split all these method into different
catalog: 3dentity, curve, spline curve. Then you will see a class
tree. There are small number methods in every node(classes in the
tree). And it's meaningfull for a geometry class library. we may do
something for all kind of curves,right? so if we use Pimpl idiom, you
can find Polymorphism.
This sounds more like letter-envelope than like compilation
firewall. But I'm not sure I've fully understood it. (The
letter-envelope idiom is used to make a class with value
semantics behave polymorphically.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 7 '08 #8
On Aug 7, 4:55*pm, James Kanze <james.ka...@gmail.comwrote:
On Aug 7, 3:51 am, feel <feel...@gmail.comwrote:
My code is a bad example ~_^. But if you can check with AcGe classes
in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
design. The special case is:
1 for geometry class, esp for leaf classes, we donot want user to
extend it. if you want to add other kind of geometry type, please add
new class.The leaf of class tree is 'sealed' class.
2 Polymorphism in this design is the Impl pointer in root class. we
can implementation the real version class for every leaf class.
3 Except for leaf class, every class can not be initialize in the
stack.in my example code, you can not do this:
base1 b;
Why? because I donot want a big class in my header file. For geometry
classes,such as AcGeNurbCurve, there are many method we have to put
into interface. but we can split all these method into different
catalog: 3dentity, curve, spline curve. Then you will see a class
tree. There are small number methods in every node(classes in the
tree). And it's meaningfull for a geometry class library. we may do
something for all kind of curves,right? so if we use Pimpl idiom, you
can find Polymorphism.

This sounds more like letter-envelope than like compilation
firewall. *But I'm not sure I've fully understood it. *(The
letter-envelope idiom is used to make a class with value
semantics behave polymorphically.)

--
James Kanze (GABI Software) * * * * * * email:james.ka...@gmail.com
Conseils en informatique orientée objet/
* * * * * * * * * *Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Yes, it's just like that. But the difference is:we have to work on
many kind of Envelops.
In classic letter-envelop, we can hide the real data deifinition. but
if we have to work
on a class heirarchy, for example:
Entity
^
|
Curve
^
|
Line

In this case, the real data is in Line, but we have to catalog all the
line's method into Entity, curve.
Then we can work on all kind of entity or curve.In a summary, we have
two requirement:
1 data hide, different data implementation.
2 class heirarchy.

Any comments about these requirements are welcome!
Aug 7 '08 #9
feel wrote:
2 Polymorphism in this design is the Impl pointer in root class. we
can implementation the real version class for every leaf class.
Your base class actually looks simply like a (well, some kind of)
smart pointer.

Yet I still fail to see where the polymorphism is, unless your base
class smart pointer is pointing to different class type, which can be
derived and have virtual functions. If it doesn't, then it's simply a
smart pointer which points to some numeric data, and that's it.

I don't believe the definition of "polymorphism" is "each object can
have differing data". If that was the case, then std::string would be
polymorphic because different instances of std::string can have
differing data (eg. different string length and contents).

In fact, I think std::string is a good comparison point. What are the
relevant differences between your classes and std::string (other than
std::string doesn't expose its private data in its protected section)?
Why? because I donot want a big class in my header file.
Is it simply a question of style? You don't want all the private data
of the class to be viewable from the header file?
Aug 7 '08 #10
"feel" <fe*****@gmail.comwrote in message
news:35**********************************@t1g2000p ra.googlegroups.com...
On 8月8æ—¥, 下åˆ4æ—¶17分, James Kanze <james.ka...@gmail.comwrote:
[...]
Thank you for your reply. I think you are right. I do not use virtual
functions as interface
because the size of object. Saying line, we can put two points as data
implementation. But if
put many virtual functinos into class, we have to get a bigger object
than that object should be:
only two points!Object's size is very important in geometry class
design.
Pimpl can make sure the object's size is suitable for this
requirements.
Polymorphism means in this case is like this: we can call curve's
getLength() function to get curve's
length. but for different kind of curves, we have to implementation
different method to compute it.
Some implementations can have a huge vtable, but the size of the object is
going to be a pointer to the vtable:

class Interface {
virtual void func1() = 0;
virtual void func2() = 0;
virtual void func3() = 0;
virtual void func4() = 0;
virtual void func5() = 0;
virtual void func6() = 0;
virtual void func7() = 0;
virtual void func8() = 0;
[on and on...];
virtual ~Interface() = 0;
};

Interface::~Interface() {}
could end of resulting in:
sizeof(Interface) == sizeof(void*);
on some impls...

Aug 9 '08 #11
On 8ÔÂ9ÈÕ, ÉÏÎç11ʱ12·Ö, "Chris M. Thomasson" <n...@spam.invalidwrote:
"feel" <feel...@gmail.comwrote in message

news:35**********************************@t1g2000p ra.googlegroups.com...
On 8ÔÂ8ÈÕ, ÏÂÎç4ʱ17·Ö, James Kanze <james.ka...@gmail.comwrote:
[...]
Thank you for your reply. I think you are right. I do not use virtual
functions as interface
because the size of object. Saying line, we can put two points as data
implementation. But if
put many virtual functinos into class, we have to get a bigger object
than that object should be:
only two points!Object's size is very important in geometry class
design.
Pimpl can make sure the object's size is suitable for this
requirements.
Polymorphism means in this case is like this: we can call curve's
getLength() function to get curve's
length. but for different kind of curves, we have to implementation
different method to compute it.

Some implementations can have a huge vtable, but the size of the object is
going to be a pointer to the vtable:

class Interface {
virtual void func1() = 0;
virtual void func2() = 0;
virtual void func3() = 0;
virtual void func4() = 0;
virtual void func5() = 0;
virtual void func6() = 0;
virtual void func7() = 0;
virtual void func8() = 0;
[on and on...];
virtual ~Interface() = 0;

};

Interface::~Interface() {}

could end of resulting in:

sizeof(Interface) == sizeof(void*);

on some impls...
En, right.But the size is still a problem. for 1000 lines, we can use
small memory if we do not use virtual functions.
Aug 9 '08 #12
On Aug 9, 4:46 am, feel <feel...@gmail.comwrote:
On 8月8æ—¥, 下åˆ4æ—¶17分, James Kanze <james.ka...@gmail.comwrote:
Thank you for your reply. I think you are right. I do not use
virtual functions as interface because the size of object.
Saying line, we can put two points as data implementation. But
if put many virtual functinos into class, we have to get a
bigger object than that object should be:
Only the first virtual function adds to the size of the object;
once the object has at least one virtual function, additional
virtual functions are free (at least in all of the
implementations I've every seen or heard of). Increased size is
not a reason to avoid virtual functions.
only two points!Object's size is very important in geometry
class design.
Pimpl can make sure the object's size is suitable for this
requirements.
The compilation firewall idiom actually increases memory use.
Either there's something I don't understand here, or you've
misunderstood something.
Polymorphism means in this case is like this: we can call
curve's getLength() function to get curve's length. but for
different kind of curves, we have to implementation different
method to compute it.
Exactly.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Aug 9 '08 #13

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

37
by: Mike Meng | last post by:
hi all, I'm a newbie Python programmer with a C++ brain inside. I have a lightweight framework in which I design a base class and expect user to extend. In other part of the framework, I heavily...
3
by: Patchwork | last post by:
Hi Everyone, Please take a look at the following (simple and fun) program: //////////////////////////////////////////////////////////////////////////// ///////////// // Monster Munch, example...
6
by: john sun | last post by:
Hello, I am not newbie C++ developer infact :). But till recently I would like dig up more about C++. I know those OO details. But when people talking OO they focused on the polymorphism, and...
4
by: Leslaw Bieniasz | last post by:
Cracow, 20.09.2004 Hello, I need to implement a library containing a hierarchy of classes together with some binary operations on objects. To fix attention, let me assume that it is a...
4
by: Leslaw Bieniasz | last post by:
Cracow, 20.10.2004 Hello, As far as I understand, the generic programming basically consists in using templates for achieving a static polymorphism of the various code fragments, and their...
7
by: Samee Zahur | last post by:
Hello, The other day I was rather shocked to find that I couldn't find a good use for runtime polymorphism! Let me explain this a bit further before you get shocked. Any function that I could...
18
by: Seigfried | last post by:
I have to write a paper about object oriented programming and I'm doing some reading to make sure I understand it. In a book I'm reading, however, polymorphism is defined as: "the ability of two...
1
weaknessforcats
by: weaknessforcats | last post by:
Introduction Polymorphism is the official term for Object-Oriented Programming (OOP). Polymorphism is implemented in C++ by virtual functions. This article uses a simple example hierarchy which...
17
by: Bart Friederichs | last post by:
Hello, I created the following inheritance: class Parent { public: void foo(int i); }; class Child : public Parent {
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.