Why doesn't this multiple virtual inheritance code compile?

  • Thread starter Chris Stankevitz
  • Start date
C

Chris Stankevitz

My intention is to
- Create an abstract base class "Shape" that must be an "Observer"
- Create an class "Square" that is a "Shape" and also an
"ObserverImp"

I thought I could do this like so:

struct Observer
{
virtual void Notify() = 0;
};

struct ObserverImp : public Observer
{
void Notify() {}
};

struct Shape : public virtual Observer
{
};

struct Square : public Shape, public ObserverImp
{
};

Shape* ShapeFactory()
{
return new Square;
}

$ g++ -Wall -c test.cpp
test.cpp: In function 'Shape* ShapeFactory()':
test.cpp:21:14: error: cannot allocate an object of abstract type
'Square'
test.cpp:16:1: note: because the following virtual functions are
pure within 'Square':
test.cpp:3:16: note: virtual void Observer::Notify()
test.cpp:22:1: warning: control reaches end of non-void function

Q: How can I do this using c++?

Thank you,

Chris
 
C

Chris Stankevitz

You could try to make this 'public virtual Observer'.

Thank you, yes I overlooked this. Although this does not help the
compile problem.
That said, I'm not at all convinced that virtual inheritance is the right
approach here.

Me too -- if it doesn't compile, it probably is not the correct
approach!

How would you go about accomplishing this with C++, if it is possible:
- Create an abstract base class "Shape" that must be an "Observer"
- Create an class "Square" that is a "Shape" and uses "ObserverImp"
to implement the "Observer" behavior.

Thank you,

Chris
 
C

Chris Stankevitz

Instantiating Square requires instantiating both an Observer and
ObserverImp under the hood.

Yes, apparently so. I naively thought using "virtual inheritance"
would make it so that both base classes need not be instantiated.
The obvious solution is to add an
implementation of the required function to Square:

I am trying to avoid that particular obvious solution as it involves
me typing the same code in all of my Shape implementations. Plus I've
already written that code: it's in ObserverImp.

Thank you,

Chris
 
I

Ian Collins

Thank you, yes I overlooked this. Although this does not help the
compile problem.


Me too -- if it doesn't compile, it probably is not the correct
approach!

How would you go about accomplishing this with C++, if it is possible:
- Create an abstract base class "Shape" that must be an "Observer"
- Create an class "Square" that is a "Shape" and uses "ObserverImp"
to implement the "Observer" behavior.

If you don't actually require dynamic binding, you could make Observer a
template with Imp as it's parameter:

template <typename Imp> struct Observer
{
void Notify() { Imp::notify(); }
};

struct Imp
{
void Notify() {}
};

struct Shape : Observer<Imp>
{
};

struct Square : Shape
{
};
 
A

Alf P. Steinbach

My intention is to
- Create an abstract base class "Shape" that must be an "Observer"
- Create an class "Square" that is a "Shape" and also an
"ObserverImp"

I thought I could do this like so:

struct Observer
{
virtual void Notify() = 0;
};

struct ObserverImp : public Observer
{
void Notify() {}
};

Use virtual inheritance for the interface (that is for `Observer`).

struct Shape : public virtual Observer
{
};

struct Square : public Shape, public ObserverImp
{
};

Technically OK.

Shape* ShapeFactory()

Please use different naming conventions for types and functions.

{
return new Square;
}

No. Forget the Java-isms. You don't need any
factory-manager-singleton-envelope-blahblah complication.

Or at least, if you absolutely have to code Java in C++, then do it
properly.

E.g., then return a smart pointer, not a raw pointer.

$ g++ -Wall -c test.cpp
test.cpp: In function 'Shape* ShapeFactory()':
test.cpp:21:14: error: cannot allocate an object of abstract type
'Square'
test.cpp:16:1: note: because the following virtual functions are
pure within 'Square':
test.cpp:3:16: note: virtual void Observer::Notify()
test.cpp:22:1: warning: control reaches end of non-void function

Q: How can I do this using c++?

See above.

It then compiles nicely, and corresponds directly to Java-like
implementation inheritance for an interface.

If you run into any problems then please post to a new thread with your
exact code and compiler invocation.


Cheers & hth.,

- Alf
 
C

Chris Stankevitz

class Shape:public observerImp{};

Paul,

I do not want all Shapes to be observerImps. I want all shapes to be
observers. I suspect what I want is not possible with c++.

Thank you,

Chris
 
C

Chris Stankevitz

If you run into any problems then please post to a new thread with your
exact code and compiler invocation.

Alf,

I attempted what you suggested (minus the style, factory, auto_ptr,
and new thread suggestions). What I came up with did not compile.
(Compiler invocation appears below). Would you please tell me how I
can modify this code so that it compiles and implements:
- Create an abstract base class "Shape" that must be an "Observer"
- Create a class "Square" that is a "Shape" and uses "ObserverImp" to
implement the "Observer" behavior.

Thank you,

Chris

//-----

struct Observer
{
virtual void Notify() = 0;
};

struct ObserverImp : public virtual Observer
{
void Notify() {}
};

struct Shape : public Observer
{
};

struct Square : public Shape, public ObserverImp
{
};

Shape* ShapeFactory()
{
return new Square;
}

//-----

$ g++ -Wall -c test.cpp
test.cpp: In function 'Shape* ShapeFactory()':
test.cpp:21:14: error: cannot allocate an object of abstract type
'Square'
test.cpp:16:1: note: because the following virtual functions are
pure within 'Square':
test.cpp:3:16: note: virtual void Observer::Notify()
test.cpp:22:1: warning: control reaches end of non-void function
 
J

Juha Nieminen

Chris Stankevitz said:
test.cpp: In function 'Shape* ShapeFactory()':
test.cpp:21:14: error: cannot allocate an object of abstract type
'Square'
test.cpp:16:1: note: because the following virtual functions are
pure within 'Square':
test.cpp:3:16: note: virtual void Observer::Notify()

How about you try to read what the compiler is telling you? It's telling
to the exact reason why you cannot instantiate the class: You have a pure
virtual function in the base class that is not implemented in the derived
class.
 
G

Goran

Strange, compiles fine with my MSVC2010 and gcc (though MSVC issues a
strange warning):

AFAICanSee, what Paavo suggested is __the__ solution for your
conundrum. Comeau also (obviously) compiles this. Chris, what is the
compiler error?

Goran.
 
M

Marcel Müller

I do not want all Shapes to be observerImps. I want all shapes to be
observers. I suspect what I want is not possible with c++.

What you are going to do is to have a helper class OberserImp that
implements the observer interface independently of the outer class. And
you want to be able to use this helper class down in the class tree not
only at the level of the interface reference in the class Shape.

I know two solution for this in C++.

1. The Java way.
The equivalent of interfaces in Java and .NET are virtual abstract base
classes in C++. If you want the Java behavior you always need to derive
virtual from all interface like C++ classes.
That was your approach except for the missing virtual at class Shape.

2. An template implememtation helper.
template <typename BASE>
class ObserverImp : BASE
{
void Notify() {}
};
class Square : public ObserverImp<Shape>
{
};

#1 has the disadvantage that it always requires another level of
indirection to access interface members at run time, even if this is not
required. And in real life this is required quite rarely.
The performance impact of this indirection is usually no big deal, but
the reduced possibilities for optimization and especially inlining could
be relevant.


Marcel
 
J

Joe keane

- Create an abstract base class "Shape" that must be an "Observer"
- Create an class "Square" that is a "Shape" and also an "ObserverImp"

So a 'Shape' is an 'Observer', and a 'Square' is a type of 'Shape'.

Now you want something that is an 'ObserverImp' [and it is square].
Wouldn't it make more sense to call it a 'SquareImp'?
 
C

Chris Stankevitz

Strange, compiles fine with my MSVC2010 and gcc (though MSVC issues a
strange warning):

Paavo,

Thank you for your help and for posting the valid source in its
entirety. I have it compiling now. The mistake I made was: missing
"virtual" in the declaration for class Shape. "Virtual" must appear
in the class declarations for "Shape" and "ObserverImp". Apparently I
had trouble parsing people's "english" description of my mistake but I
had no trouble parsing the c++ code in its entirety.

The code that compiles and the g++ invocation appear below.

Thank you again,

Chris

===


struct Observer
{
virtual void Notify() = 0;
};

struct ObserverImp : public virtual Observer
{
void Notify() {}
};

struct Shape : public virtual Observer
{
};

struct Square : public Shape, public ObserverImp
{
};

Shape* ShapeFactory()
{
return new Square;
}

====

$ g++ -Wall -c test.cpp
 
C

Chris Stankevitz

Your code was missing one virtual modifier.

Also you need to declare the base classes destructors as virtual.
Seehttp://en.wikipedia.org/wiki/Virtual_function#Virtual_destructors
for an explanation.

Spike,

Thank you. I tried to dumb down my example to make the code easier to
understand. My actual code has virtual destructors and, Alf will be
happy to hear, a consistent style that differentiates class names from
function names.

Chris
 
A

Alf P. Steinbach

For good examples of using the same naming convention for both types and
functions see the C++ standard library.

The standard library operates under conditions that normal code does not:

(a) It is guaranteed to be used a lot by everybody.

(a1) all serious users become familiar with the names. This means
that for the standard library it is more important to reduce typing
effort (having short names) than to reduce interpretation and
recognition effort (having self-describing names). Aiming for the latter
would just be wasted effort.

(a2) since much of it is template based code, short names and terse
coding style help to reduce compile times. look at the code written pjp
(e.g. for visual c++'s standard library). it is horrible by the
standards of ordinary application programming, where clarity is the main
concern, but it it is near ideal for fast compilation.

(a3) bugs are much more likely to surface early and repeatedly, than
with ordinary application code. again this reduced the need for clarity.

(b) It can be and in some parts has to be system- and compiler-specific.
If some particular piece of code is not part of the developers have
aimed to make portable (to reduce the total effort), then it can be
written in any compiler- and system-dependent way, whatever the
developer regards as easiest. This is generally not so for ordinary
application code.

In short, do not look to the standard library for naming and formatting
conventions for ordinary application code.

The standard library is not ordinary application code: it operates under
a totally different set of rules.

Cheers & hth.,

- Alf
 
I

Ian Collins

There can only be one response to the above verbiage: LOL!

Why?

I've worked on a lot of projects with almost as many coding standards
and I don't think the standard library naming conventions would be
acceptable under any of them. All of my current clients have some form
of different naming convention for types rule.
 
J

Joe keane

The standard library operates under conditions that normal code does not:

c) It has a precise specification, that is more or less set in stone.

Often apps do not have much documentation [especially of internals], and
what there is may be inaccurate or out of date. So it is important for
the code to explain itself.

I would not mind if the standard headers have -no- comments. So long as
it works right...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top