Virtual deconstructors in interface->abstract->concrete class design - c++

I have tried to answer this myself, by looking up several questions at StackOverflow. And although I think I understand this correctly, I can't fix this. Which, leaves me with the only obvious observation: I still don't get it.
I have made a summary of the questions at the bottom of this post, everything in between is information I have gathered and context for this question.
So, I get it that when you have a base class, and a derived class, your deconstructor should be marked virtual in the base class. To allow polymorphism.
But, I cannot seem to get my code to compile, or when it does compile, it does not link due 'undefined references'. I have been changing back and forth, but I never seem to get out of this cycle.
Basically I have an interace, defined like this:
#ifndef GUIELEMENT_H_
#define GUIELEMENT_H_
class GuiElement {
public:
virtual ~GuiElement();
virtual void draw() = 0;
};
#endif /* GUIELEMENT_H_ */
I have several objects extending from this. A simple relation is GuiWindow (directly derives from GuiElement):
#ifndef CGUIWINDOW_H_
#define CGUIWINDOW_H_
#include <assert.h>
#include <cstddef>
#include "../GuiElement.h"
#include "../GuiInteractionDelegate.h"
class GuiWindow : public GuiElement {
public:
GuiWindow(GuiInteractionDelegate * guiInteractionDelegate) {
assert(guiInteractionDelegate);
interactionDelegate = guiInteractionDelegate;
}
~GuiWindow() {
//delete interactionDelegate;
}
// called each frame, delegates its behavior to the given concrete cGuiWindowDelegate class.
void interact() {
interactionDelegate->interact(this);
}
private:
GuiInteractionDelegate * interactionDelegate;
};
#endif /* CGUIWINDOW_H_ */
This code does not link, gives me:
undefined reference to `GuiElement::~GuiElement()'
I thought it was sufficient to have an implementation in the GuiWindow class? Is that correct?
The next thing, which is really bugging me, is that I also have an abstract class derived from GuiElement, and concrete implementations on top of that. Basically giving:
GuiElement->GuiShape->GuiButton
Here is the header of GuiShape:
#ifndef GUISHAPE_H_
#define GUISHAPE_H_
#include "../GuiElement.h"
#include "../../gameobjects/Rectangle.h"
class GuiShape : public GuiElement {
public:
GuiShape(Rectangle * rect);
GuiShape(int x, int y, int width, int height);
~GuiShape();
void draw();
void setX(int value) { rectangle->setStartX(value); }
void setY(int value) { rectangle->setStartY(value); }
Rectangle * getRectangle() { return rectangle; }
bool isMouseOverShape();
void setColors(int darkBorder, int lightBorder, int inner);
int getDarkBorderColor() { return darkBorderColor; }
int getLightBorderColor() { return lightBorderColor; }
int getInnerColor() { return innerColor; }
protected:
Rectangle * rectangle;
private:
bool rectangleOwner;
int darkBorderColor;
int lightBorderColor;
int innerColor;
};
And finally GuiButton:
#ifndef CGUIBUTTON_H_
#define CGUIBUTTON_H_
#include <sstream>
#include <string>
#include "allegro.h"
#include "../../gameobjects/Rectangle.h"
#include "GuiShape.h"
class GuiButton : public GuiShape {
public:
GuiButton(Rectangle * rect, std::string theLabel);
GuiButton(int x, int y, int width, int height, std::string theLabel);
~GuiButton();
void draw();
std::string * getLabel() {
return label;
}
BITMAP * getBitmap() { return bitmap; }
void setBitmap(BITMAP * value) { bitmap = value; }
void setHasBorders(bool value) { hasBorders = value; }
void setPressed(bool value) { pressed = value; }
bool shouldDrawPressedWhenMouseHovers() { return drawPressedWhenMouseHovers; }
bool shouldDrawBorders() { return hasBorders; }
void setDrawPressedWhenMouseHovers(bool value) { drawPressedWhenMouseHovers = value; }
bool isPressed() { return pressed; }
private:
std::string * label;
bool drawPressedWhenMouseHovers;
bool hasBorders;
bool pressed;
BITMAP * bitmap;
void drawBackground();
void drawLighterBorder();
void drawDarkerBorder();
void drawButtonUnpressed();
void drawButtonPressed();
};
#endif /* CGUIBUTTON_H_ */
Which leads me to the following questions:
What is the best way to use virtual deconstructors where objects are derived from A->B->C ?
Should C only be the concrete virtual? And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)
Why would I get 'undefined references' with the straight-forward implementation of A->B ? (GuiElement->GuiWindow)
Thanks in advance for your help!

What is the best way to use virtual deconstructors where objects are derived from A->B->C ?
mark the base's (or all) destructor as virtual.
Should C only be the concrete virtual? And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)
Not sure what you mean by "concrete virtual" but a class with members that need destroying should destroy them in it's own destructor. No exceptions. when ~C is called, it destroys it's own stuff, and then ~B will be called automatically. The virtual just makes absolutely sure that ~C is called first.
Why would I get 'undefined references' with the straight-forward implementation of A->B ? (GuiElement->GuiWindow)
virtual ~GuiElement(); tells the compiler that the class has a destructor that will be defined later. You wanted either:
// There is no definition, cannot make a local "GuiElement" variable
// They can only make local "GuiButton" or other derived.
// You can still have pointers to a GuiElement.
// This is called "pure virtual"
virtual ~GuiElement() = 0;
or:
// There is a definition, someone can make a local "GuiElement" variable
virtual ~GuiElement() {};

I thought it was sufficient to have an implementation in the GuiWindow class? Is that correct?
No. A virtual function (that is not pure virtual, as your destructor of GuiElement) must be defined if it is declared in the class.
Destructors go even further: they must be implemented, always, even if it is pure virtual[1]. If you hadn't declared it, the compiler would create one (implicitly nonvirtual, but would be virtual if it would override a virtual destructor) for you. In C++11, you can just mark it "defaulted" (which means "compiler, implement that for me") and "deleted" which means "the program may never, implicitly or explicitly, destruct objects of this type".
What is the best way to use virtual deconstructors where objects are derived from A->B-
C ?
You usually want to make the topmost base's destructor virtual, that means all destructors in the hierarchy are virtual.
And if so, how do you release resources defined and handled only in B? (A=GuiElement, B=GuiShape, C=GuiButton)
In ~B(), naturally.
[1] :
12.4/7: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
derived class are created in the program, the destructor shall be defined. If a class has a base class with a
virtual destructor, its destructor (whether user- or implicitly- declared) is virtual.

Related

Undefined reference to `vtable' in the constructor [duplicate]

This question already has answers here:
undefined reference to vtable [duplicate]
(2 answers)
Closed 5 years ago.
I try to learn C++ constructors, Imma noobie yet. I wrote the next class:
screen.h
#ifndef SCREEN_H
#define SCREEN_H
#include "pch.h"
class Screen
{
public:
Screen(const std::string& name);
Screen(const Screen& screen);
Screen(Screen&& screen);
const std::string& name() const;
virtual void draw();
private:
std::string m_name;
};
#endif // SCREEN_H
screen.cpp
#include "screen.h"
Screen::Screen(const std::string& name)
: m_name{name}
{
m_name = name;
}
Screen::Screen(const Screen& screen)
: m_name{screen.m_name}
{
}
Screen::Screen(Screen&& screen)
: m_name{std::move(screen.m_name)}
{
}
const std::string& Screen::name() const
{
return this->m_name;
}
But I get an issue in the compilation time:
screen.cpp:4: error: undefined reference to `vtable for Screen'
And so for all constructors.
I can't get that is my mistake... Pls, can anyone explain me?
You must also implement Screen::draw.
In typical implementations, a constructor of a polymorphic class sets up a pointer to the class's "vtable", which contains pointers to the class's virtual functions. Since your first (and only) virtual function is missing, the compiler can't produce this vtable, and so all the constructors complain about it at link time.
What aschepler said is absolutely correct. Concerned about your begining, you may want some advice when a virtual function should be used though. Vitual function is used as a method to support polymorphism in cpp, and can be divided into two using scenario.
Interface/ abstract class
In this scenario, virtual function was declared as pure virtual, with which one class would be called as abstract class and non-instancable. By doing this, you can implement 'interface' like most modern programming support.
class Interface {
//....
virtual void f() = 0;
};
class Concrete: public Interface {
// override this f()
void f() override {}
};
Polymorphism/ concrete class
In this scenario, virtual function was declared as normal function except it can be override by derived class. And you must implement it.
class Parent {
//...
virtual void g();
}
class Derived: public Parent {
//...
void g() override{}
}
Note that you can still declare a function with the same name of parent, which was not declared as virtual function. This would be called hide, and is another topic.

Can I force a class to be inherited?

Consider the following class:
//! Counter class base. Must be inherited.
class base_seeded_counter
{
public:
//! Destructor
virtual ~base_seeded_counter() { };
//! Get the next value.
int get_next();
protected:
//! Override the counter next value.
void set_next(const int next);
private:
int m_next_value; // Next value.
}
// ------------------------------------------------------
inline int base_seeded_counter::get_next()
{
return m_next_value++;
}
// ------------------------------------------------------
inline void base_seeded_counter::set_next(const int next)
{
m_next_value = next;
}
The purpose of this class is to implement a base counter object with the intention that it must be inherited. It does not have any virtual methods other than the destructor and, more importantly, does not initialize the m_next_value member. This is the job of the derived class. For example:
class file_seeded_counter : public base_seeded_counter
{
public:
file_seeded_counter(const std::string &file_name);
void reseed();
private:
std::string m_file_name;
}
inline int file_seeded_counter::file_seeded_counter(const std::string &file_name) :
m_file_name(file_name)
{
reseed();
}
inline void file_seeded_counter::reseed()
{
int seed_from_file;
// open file here and get seed value...
set_next(seed_from_file);
}
This class, deriving from base_seeded_counter reads the initial counter value from a file, and offers the ability to re-read the seed from the file via the reseed() method. There may be other classes that offer similar functionality to seed from databases, network sources, or a PRNG, for example.
My question is this: Given that I have no pure virtual methods, does C++ offer a mechanism to prevent someone from creating an instance of base_seeded_counter?
My question is this: Given that I have no pure virtual methods, does C++ offer a mechanism to prevent someone from creating an instance of base_seeded_counter?
Yes, give it a protected default constructor (may be empty).
Given that you already have a virtual destructor, the obvious way would be to declare it pure virtual. It's entirely legitimate to have a pure virtual destructor that's also defined in the base class:
class base_seeded_counter {
public:
virtual ~base_seeded_counter() = 0;
int get_next();
protected:
void set_next(const int next);
private:
int m_next_value;
}
inline virtual base_seeded_counter::~base_seeded_counter() {}
Defining the destructor this way doesn't change the fact that it's a pure virtual, so this class can't be instantiated.
As an aside, the comments you currently have in the code are 100% useless visual noise. A comment of get next on a function named get_next adds nothing useful at all.

Calling virtual method from base class C++

I'm new to C++ and i'm having a hard time figuring out what's wrong with my virtual functions. So, here's what i have:
GEntity.h
class GEntity
{
public:
//...
virtual void tick(void);
virtual void render(void);
//...
};
GEntity.cpp
//...
void GEntity::tick(void){}
void GEntity::render(void){}
//...
GLiving.h
class GLiving : public GEntity
{
public:
//...
virtual void tick(void);
virtual void render(void);
//...
};
GLiving.cpp
//...
void GEntity::tick(void){}
void GEntity::render(void){}
//...
Then i have other classes that derive from GLiving (Player, Enemy) which implement their own versions of this two methods:
Player.h
class Player : public GLiving
{
public:
//...
void tick(void);
void render(void);
//...
};
Player.cpp
//...
void GEntity::tick(void)
{
//Here there's some actual code that updates the player
}
void GEntity::render(void)
{
//Here there's some actual code that renders the player
}
//...
Now, if i declare an object of class Player, and call the render/tick method, everything goes well, but i am in a situation in which i add my player to an arraylist (a struct i created) of GEntity, and then, when i get it back, i get it as a GEntity, and i need to call the render/tick methods without knowing it's derived class...
I've tried with the code above, but i get an access violation in the line where i call either the render or tick method, on the extracted GEntity...
...is what i want even possible to achieve?
(sorry if my english is not so good, but i'm italian)
If you have an array of GEntity then, each time you "add" a derived type, the equivalent of this happens:
GEntity g;
Player p;
g = p; // object slicing, you assigned a Player to a GEntity object.
g.render(); // GEntity::render() gets called
On the other hand, you can use a pointer to a base class to access a derived method:
GEntity* g;
Player p;
g = &p;
g->render(); // calls Player::render()
So a way to deal with polymorphism in containers is to have arrays/containers of (preferably smart) pointers to the base class. This example uses raw pointers for simplicity, but you should use smart pointers in real code:
std::vector<CEntity*> entities;
entities.push_back(new Player);
entities.push_back(new GLiving);
// some c++11
for ( auto e : entities) {
e->render();
}

C++ - "Member function not declared" in derived class

I have a problem in MSVC++ 2008 where VS2008 is throwing this compile error:
error C2509: 'render' : member function not declared in 'PlayerSpriteKasua'
Now, what's confusing me is that render() is defined, but in an inherited class.
The class definition works like this:
SpriteBase -Inherited By-> PlayerSpriteBase -Inherited By-> PlayerSpriteKasua
So, a pared-down version of SpriteBase.h is the following:
class SpriteBase {
public:
//Variables=============================================
-snip-
//Primary Functions=====================================
virtual void think()=0; //Called every frame to allow the sprite to process events and react to the player.
virtual void render(long long ScreenX, long long ScreenY)=0; //Called every frame to render the sprite.
//Various overridable and not service/event functions===
virtual void died(); //Called when the sprite is killed either externally or via SpriteBase::kill().
-snip-
//======================================================
};
PlayerSpriteBase.h is this:
class PlayerSpriteBase : public SpriteBase
{
public:
virtual void pose() = 0;
virtual void knockback(bool Direction) = 0;
virtual int getHealth() = 0;
};
And finally, PlayerSpriteKasua.h is this:
class PlayerSpriteKasua : public PlayerSpriteBase
{
public:
};
I know there are no members in it yet, but that's simply because I hadn't gotten to adding them. Same goes for PlayerSpriteBase; there's other stuff left to go in to it.
The code in PlayerSpriteKasua.cpp is this:
#include "../../../MegaJul.h" //Include all the files needed in one go
void PlayerSpriteKasua::render(long long ScreenX, long long ScreenY) {
return;
}
void PlayerSpriteKasua::think() {
return;
}
int PlayerSpriteKasua::getHealth() {
return this->Health;
}
When I type, say, void PlayerSpriteKasua::, Intellisense pops up listing all the members of PlayerSpriteBase and SpriteBase just fine, but on compile it fails like I said above.
Is there any particular reason I'm getting this error?
PlayerSpriteBase.cpp is empty and has nothing in it as of yet.
SpriteBase.cpp has plenty of function definitions for SpriteBase, and uses the same format as PlayerSpriteKasua.cpp:
void SpriteBase::died() {
return;
}
is an example.
In PlayerSpriteKasua.h you need to re-declare whatever methods you're going to override/implement (without the "=0" to say that those methods are not abstract anymore). So you need to write it like follows:
class PlayerSpriteKasua : public PlayerSpriteBase
{
public:
virtual void think();
virtual void render(long long ScreenX, long long ScreenY);
virtual int getHealth();
};
...or did you omit that to keep your post shorter?
You need to provide a declaration for PlayerSpriteKasua::render() in your class definition. Otherwise, other translation units including your PlayerSpriteKasua.h wouldn't be able to tell that you'd provided a definition, and would be forced to conclude that PlayerSpriteKasua can't be instantiated.
You need to redeclare the members of SpriteBase that you are going to implement in PlayerSpriteKasua in the declaration of PlayerSpriteKasua in PlayerSpriteKasua.h.

PIMPL problem: How to have multiple interfaces to the impl w/o code duplication

I have this pimpl design where the implementation classes are polymorphic but the interfaces are supposed to just contain a pointer, making them polymorphic somewhat defeats the purpose of the design.
So I create my Impl and Intf base classes to provide reference counting. And then the user can create their implementations. An example:
class Impl {
mutable int _ref;
public:
Impl() : _ref(0) {}
virtual ~Impl() {}
int addRef() const { return ++_ref; }
int decRef() const { return --_ref; }
};
template <typename TImpl>
class Intf {
TImpl* impl;
public:
Intf(TImpl* t = 0) : impl(0) {}
Intf(const Intf& other) : impl(other.impl) { if (impl) impl->addRef(); }
Intf& operator=(const Intf& other) {
if (other.impl) other.impl->addRef();
if (impl && impl->decRef() <= 0) delete impl;
impl = other.impl;
}
~Intf() { if (impl && impl->decRef() <= 0) delete impl; }
protected:
TImpl* GetImpl() const { return impl; }
void SetImpl(... //etc
};
class ShapeImpl : public Impl {
public:
virtual void draw() = 0;
};
class Shape : public Intf<ShapeImpl> {
public:
Shape(ShapeImpl* i) : Intf<ShapeImpl>(i) {}
void draw() {
ShapeImpl* i = GetImpl();
if (i) i->draw();
}
};
class TriangleImpl : public ShapeImpl {
public:
void draw();
};
class PolygonImpl : public ShapeImpl {
public:
void draw();
void addSegment(Point a, Point b);
};
Here is where have the issue. There are two possible declaration for class Polygon:
class Polygon1 : public Intf<PolygonImpl> {
public:
void draw() {
PolygonImpl* i = GetImpl();
if (i) i->draw();
}
void addSegment(Point a, Point b) {
PolygonImpl* i = GetImpl();
if (i) i->addSegment(a,b);
}
};
class Polygon2 : public Shape {
void addSegment(Point a, Point b) {
ShapeImpl* i = GetImpl();
if (i) dynamic_cast<Polygon*>(i)->addSegment(a,b);
}
}
In the Polygon1, I have rewrite the code for draw because I have not inherited it. In Polygon2 I need ugly dynamic casts because GetImpl() doesn't know about PolygonImpl. What I would like to do is something like this:
template <typename TImpl>
struct Shape_Interface {
void draw() {
TImpl* i = GetImpl();
if (i) i->draw();
}
};
template <typename TImpl>
struct Polygon_Interface : public Shape_Interface<Timpl> {
void addSegment(Point a, Point b) { ... }
};
class Shape : public TIntf<ShapeImpl>, public Shape_Interface<ShapeImpl> {...};
class Polygon : public TIntf<PolygonImpl>, public Polygon_Interface<PolygonImpl> {
public:
Polygon(PolygonImpl* i) : TIntf<PolygonImpl>(i) {}
};
But of course there's a problem here. I can't access GetImpl() from the Interface classes unless I derive them from Intf. And if I do that, I need to make Intf virtual everywhere it appears.
template <typename TImpl>
class PolygonInterface : public virtual Intf<TImpl> { ... };
class Polygon : public virtual Intf<PolygonImpl>, public PolygonInterface { ... }
OR I can store a TImpl*& in each Interface and construct them with a reference to the base Intf::impl. But that just means I have a pointer pointing back into myself for every interface included.
template <typename TImpl>
class PolygonInterface {
TImpl*& impl;
public:
PolygonInterface(TImpl*& i) : impl(i) {}
...};
Both of these solutions bloat the Intf class, add an extra dereference, and basically provide no benefit over straight polymorphism.
So, the question is, is there a third way, that I've missed that would solve this issue besides just duplicating the code everywhere (with its maintenance issues)?
TOTALLY SHOULD, BUT DOESN'T WORK: I wish there were base classes unions that just overlaid the class layouts and, for polymorphic classes, required that they have the exact same vtable layout. Then both Intf and ShapeInterface would each declare a single T* element and access it identically:
class Shape : public union Intf<ShapeImpl>, public union ShapeInterface<ShapeImpl> {};
I should note that your Impl class is nothing more than the reimplementation of a shared_ptr without the thread safety and all those cast bonuses.
Pimpl is nothing but a technic to avoid needless compile-time dependencies.
You do not need to actually know how a class is implemented to inherit from it. It would defeat the purpose of encapsulation (though your compiler does...).
So... I think that you are not trying to use Pimpl here. I would rather think this is a kind of Proxy patterns, since apparently:
Polygon1 numberOne;
Polygon2 numberTwo = numberOne;
numberTwo.changeData(); // affects data from numberOne too
// since they point to the same pointer!!
If you want to hide implementation details
Use Pimpl, but the real one, it means copying in depth during copy construction and assignment rather than just passing the pointer around (whether ref-counted or not, though ref-counted is preferable of course :) ).
If you want a proxy class
Just use a plain shared_ptr.
For inheritance
It does not matter, when you inherit from a class, how its private members are implemented. So just inherit from it.
If you want to add some new private members (usual case), then:
struct DerivedImpl;
class Derived: public Base // Base implemented with a Pimpl
{
public:
private:
std::shared_ptr<DerivedImpl> _data;
};
There is not much difference with classic implementation, as you can see, just that there is a pointer in lieu of a bunch of data.
BEWARE
If you forward declare DerivedImpl (which is the goal of Pimpl), then the destructor automatically generated by the compiler is... wrong.
The problem is that in order to generate the code for the destructor, the compiler needs the definition of DerivedImpl (ie: a complete type) in order to know how to destroy it, since a call to delete is hidden in the bowels of shared_ptr. However it may only generate a warning at compilation time (but you'll have a memory leak).
Furthermore, if you want an in-depth copy (rather than a shallow one, which consists in the copy and the original both pointing to the same DerivedImpl instance), you will also have to define manually the copy-constructor AND the assignment operator.
You may decide to create a better class that shared_ptr which will have deep-copy semantics (which could be called member_ptr as in cryptopp, or just Pimpl ;) ). This introduce a subtle bug though: while the code generated for the copy-constructor and the assignement operator could be thought of as correct, they are not, since once again you need a complete type (and thus the definition of DerivedImpl), so you will have to write them manually.
This is painful... and I'm sorry for you.
EDIT: Let's have a Shape discussion.
// Shape.h
namespace detail { class ShapeImpl; }
class Shape
{
public:
virtual void draw(Board& ioBoard) const = 0;
private:
detail::ShapeImpl* m_impl;
}; // class Shape
// Rectangle.h
namespace detail { class RectangleImpl; }
class Rectangle: public Shape
{
public:
virtual void draw(Board& ioBoard) const;
size_t getWidth() const;
size_t getHeight() const;
private:
detail::RectangleImpl* m_impl;
}; // class Rectangle
// Circle.h
namespace detail { class CircleImpl; }
class Circle: public Shape
{
public:
virtual void draw(Board& ioBoard) const;
size_t getDiameter() const;
private:
detail::CircleImpl* m_impl;
}; // class Circle
You see: neither Circle nor Rectangle care if Shape uses Pimpl or not, as its name implies, Pimpl is an implementation detail, something private that is not shared with the descendants of the class.
And as I explained, both Circle and Rectangle use Pimpl too, each with their own 'implementation class' (which can be nothing more than a simple struct with no method by the way).
I think you were right in that I didn't understand your question initially.
I think you're trying to force a square shape into a round hole... it don't quite fit C++.
You can force that your container holds pointers to objects of a given base-layout, and then allow objects of arbitrary composition to be actually pointed to from there, assuming that you as a programmer only actually place objects that in fact have identical memory layouts (member-data - there's no such thing as member-function-layout for a class unless it has virtuals, which you wish to avoid).
std::vector< boost::shared_ptr<IShape> > shapes;
NOTE at the absolute MINIMUM, you must still have a virtual destructor defined in IShape, or object deletion is going to fail miserably
And you could have classes which all take a pointer to a common implementation core, so that all compositions can be initialized with the element that they share (or it could be done statically as a template via pointer - the shared data).
But the thing is, if I try to create an example, I fall flat the second I try to consider: what is the data shared by all shapes? I suppose you could have a vector of Points, which then could be as large or small as any shape required. But even so, Draw() is truly polymorphic, it isn't an implementation that can possibly be shared by multiple types - it has to be customized for various classifications of shapes. i.e. a circle and a polygon cannot possibly share the same Draw(). And without a vtable (or some other dynamic function pointer construct), you cannot vary the function called from some common implementation or client.
Your first set of code is full of confusing constructs. Maybe you can add a new, simplified example that PURELY shows - in a more realistic way - what you're trying to do (and ignore the fact that C++ doesn't have the mechanics you want - just demonstrate what your mechanic should look like).
To my mind, I just don't get the actual practical application, unless you're tyring to do something like the following:
Take a COM class, which inherits from two other COM Interfaces:
class MyShellBrowserDialog : public IShellBrowser, public ICommDlgBrowser
{
...
};
And now I have a diamond inheritence pattern: IShellBrowser inherits ultimately from IUnknown, as does ICommDlgBrowser. But it seems incredibly silly to have to write my own IUnknown:AddRef and IUnknown::Release implementation, which is a highly standard implementation, because there's no way to cause the compiler to let another inherited class supply the missing virtual functions for IShellBrowser and/or ICommDlgBrowser.
i.e., I end up having to:
class MyShellBrowserDialog : public IShellBrowser, public ICommDlgBrowser
{
public:
virtual ULONG STDMETHODCALLTYPE AddRef(void) { return ++m_refcount; }
virtual ULONG STDMETHODCALLTYPE Release(void) { return --m_refcount; }
...
}
because there's no way I know of to "inherit" or "inject" those function implementations into MyShellBrowserDialog from anywhere else which actually fill-in the needed virtual member function for either IShellBrowser or ICommDlgBrowser.
I can, if the implementations were more complex, manually link up the vtable to an inherited implementor if I wished:
class IUnknownMixin
{
ULONG m_refcount;
protected:
IUnknonwMixin() : m_refcount(0) {}
ULONG AddRef(void) { return ++m_refcount; } // NOTE: not virutal
ULONG Release(void) { return --m_refcount; } // NOTE: not virutal
};
class MyShellBrowserDialog : public IShellBrowser, public ICommDlgBrowser, private IUnknownMixin
{
public:
virtual ULONG STDMETHODCALLTYPE AddRef(void) { return IUnknownMixin::AddRef(); }
virtual ULONG STDMETHODCALLTYPE Release(void) { return IUnknownMixin::Release(); }
...
}
And if I needed the mix-in to actually refer to the most-derived class to interact with it, I could add a template parameter to IUnknownMixin, to give it access to myself.
But what common elements could my class have or benefit by that IUnknownMixin couldn't itself supply?
What common elements could any composite class have that various mixins would want to have access to, which they needed to derive from themselves? Just have the mixins take a type parameter and access that. If its instance data in the most derived, then you have something like:
template <class T>
class IUnknownMixin
{
T & const m_outter;
protected:
IUnknonwMixin(T & outter) : m_outter(outter) {}
// note: T must have a member m_refcount
ULONG AddRef(void) { return ++m_outter.m_refcount; } // NOTE: not virtual
ULONG Release(void) { return --m_outter.m_refcount; } // NOTE: not virtual
};
Ultimately your question remains somewhat confusing to me. Perhaps you could create that example that shows your preferred-natural-syntax that accomplishes something clearly, as I just don't see that in your initial post, and I can't seem to sleuth it out from toying with these ideas myself.
I have seen lots of solutions to this basic conundrum: polymorphism + variation in interfaces.
One basic approach is to provide a way to query for extended interfaces - so you have something along the lines of COM programming under Windows:
const unsigned IType_IShape = 1;
class IShape
{
public:
virtual ~IShape() {} // ensure all subclasses are destroyed polymorphically!
virtual bool isa(unsigned type) const { return type == IType_IShape; }
virtual void Draw() = 0;
virtual void Erase() = 0;
virtual void GetBounds(std::pair<Point> & bounds) const = 0;
};
const unsigned IType_ISegmentedShape = 2;
class ISegmentedShape : public IShape
{
public:
virtual bool isa(unsigned type) const { return type == IType_ISegmentedShape || IShape::isa(type); }
virtual void AddSegment(const Point & a, const Point & b) = 0;
virtual unsigned GetSegmentCount() const = 0;
};
class Line : public IShape
{
public:
Line(std::pair<Point> extent) : extent(extent) { }
virtual void Draw();
virtual void Erase();
virtual void GetBounds(std::pair<Point> & bounds);
private:
std::pair<Point> extent;
};
class Polygon : public ISegmentedShape
{
public:
virtual void Draw();
virtual void Erase();
virtual void GetBounds(std::pair<Point> & bounds);
virtual void AddSegment(const Point & a, const Point & b);
virtual unsigned GetSegmentCount() const { return vertices.size(); }
private:
std::vector<Point> vertices;
};
Another option would be to make a single richer base interface class - which has all the interfaces you need, and then to simply define a default, no-op implementation for those in the base class, which returns false or throws to indicate that it isn't supported by the subclass in question (else the subclass would have provided a functional implementation for this member function).
class Shape
{
public:
struct Unsupported
{
Unsupported(const std::string & operation) : bad_op(operation) {}
const std::string & AsString() const { return bad_op; }
std::string bad_op;
};
virtual ~Shape() {} // ensure all subclasses are destroyed polymorphically!
virtual void Draw() = 0;
virtual void Erase() = 0;
virtual void GetBounds(std::pair<Point> & bounds) const = 0;
virtual void AddSegment(const Point & a, const Point & b) { throw Unsupported("AddSegment"); }
virtual unsigned GetSegmentCount() const { throw Unsupported("GetSegmentCount"); }
};
I hope that this helps you to see some possibilities.
Smalltalk had the wonderful attribute of being able to ask the meta-type-system whether a given instance supported a particular method - and it supported having a class-handler that could execute anytime a given instance was told to perform an operation it didn't support - along with what operation that was, so you could forward it as a proxy, or you could throw a different error, or simply quietly ignore that operation as a no-op).
Objective-C supports all of those same modalities as Smalltalk! Very, very cool things can be accomplished by having access to the type-system at runtime. I assume that .NET can pull of some crazy cool stuff along those lines (though I doubt that its nearly as elegant as Smalltalk or Objective-C, from what I've seen).
Anyway, ... good luck :)