1

It seems that in C++ (or is it general OOP concept?) the once virtual always virtual thing holds. I was wondering if there is anyway it can be stopped. I have this situation that needs what I am asking.

Let's say I have a Factory class like so

class Object;
class GeneralFactory
{
private:
    virtual Object* CreateObject() = 0;
};

Now I have a specialized factory which still lets object creation but with more control.

class SpecializedFactory : public GeneralFactory
{
private:
    virtual Object* CreateObject();
    virtual Object* DoCreateObject() = 0;
};

The idea is for the SpecializedFactory::CreateObject to use DoCreateObject in its implementation. But the way things are, a class can derive from SpecializedFactory and override the CreateObject undermining the good intentions.

Is what I am looking for possible, and valid, or the only way is for programmers to be good citizens and follow class hierarchy rules?

What is a suitable alternative? I am working with C++03 FYI.

Dirt
  • 181
  • 2
  • 12

2 Answers2

3

What you need is final keyword:

struct base {
   virtual void f();
};
struct derived : base {
   void f() final;       // virtual as it overrides base::f
};
struct mostderived : derived {
   //void f();           // error: cannot override!
};
Krypton
  • 3,337
  • 5
  • 32
  • 52
  • then I guess there is no way as this is C++ feature. – Krypton Aug 30 '14 at 07:08
  • @Dirt: If `final` was unnecessary then they wouldn't have added it to the next version of the language... – user541686 Aug 30 '14 at 07:23
  • @Mehrdad I don't understand what made you comment "If final was unnecessary then they wouldn't have added it to the next version of the language.." to me. I didn't say it was unnecessary! – Dirt Aug 30 '14 at 07:56
  • @Dirt: I meant if there was a way to achieve it in C++03 then it wouldn't have been necessary for them to introduce it in C++11. – user541686 Aug 30 '14 at 08:14
  • 1
    @Mehrdad that logic doesn't hold; there are lots of things in C++11 that were possible in C++03, but simpler to express (e.g. iterating over a container) – M.M Aug 30 '14 at 08:29
  • @MattMcNabb: Yeah it doesn't in general, but in this particular case it does hold, given that the sole purpose of `final` is to prevent overriding. There aren't any other factors that come into play with `final` (no added convenience or anything), unlike with other features. – user541686 Aug 30 '14 at 08:53
3

It seems that in C++ (or is it general OOP concept?) the once virtual always virtual thing holds.

I would not call it "once virtual always virtual", because that sounds a bit misleading. Virtuality is simply not the business of derived classes. Virtual functions are all about base clasess. It is the base class which needs to know whether a function is virtual or not, and make a virtual function call if necessary. The derived-class function may say "I am not virtual anymore!", but who cares? It's been invoked through a virtual function call already at that point.

C++11 final does not change anything about this run-time behaviour, it just prevents overriding at compile time.

Is what I am looking for possible, and valid, or the only way is for programmers to be good citizens and follow class hierarchy rules?

In C++03, the easiest way would be good documentation and selecting only good programmers at job interviews :)

But that's probably not what you are looking for. A technical solution here would be to change your class design to a has-a relationship. Workarounds to make entire classes final do exist in C++03.

So, rather than ConcreteSpecializedFactory is-a SpecializedFactory, make it SpecializedFactory has-a SpecializedFactoryImplementation. You can then (optionally, for even more strictness) use friend to allow the latter to be called only from the former, and (and this is where the interesting part comes) you can use the virtual inheritance trick from the C++ FAQ "How can I set up my class so it won't be inherited from?" to make the entire SpecializedFactory class final.

class SpecializedFactoryImplementation
{
public:
    virtual ~SpecializedFactoryImplementation() {}

private:
    SpecializedFactoryImplementation(SpecializedFactoryImplementation const &);
    SpecializedFactoryImplementation &operator=(SpecializedFactoryImplementation const &);

    friend class SpecializedFactory;

    Object* CreateObject()
    {
        return DoCreateObject();
    }

    virtual Object* DoCreateObject() = 0;
};

class SpecializedFactoryBase
{
private:
    friend class SpecializedFactory;
     SpecializedFactoryBase() {}
};

class SpecializedFactory : public GeneralFactory, private virtual SpecializedFactoryBase
{
// ...
public:
    SpecializedFactory(SpecializedFactoryImplementation* impl) :
        m_impl(impl)
    {
        // null checking omitted for simplicity
    }
private:

    // the GeneralFactory base class should not be copyable
    // anyway, so we do not have to worry about copy constructor
    // or assignment operator

    SpecializedFactoryImplementation* const m_impl;

    virtual Object* CreateObject()
    {
        return m_impl->CreateObject();
    }
};

The following will then not compile:

class SpecializedFactoryWrittenByEvilProgrammer : public SpecializedFactory
{
public:
    SpecializedFactoryWrittenByEvilProgrammer() : SpecializedFactory(0) {}
private:
    virtual Object* CreateObject()
    {
        return 0;
    }
};

And the following will not compile, either:

// somewhere outside of SpecializedFactory:
SpecializedFactoryImplementation *s;
s->CreateObject();
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • Looks like a nice start. Thanks. – Dirt Sep 01 '14 at 18:41
  • A possible downside with the virtual inheritance trick is that it may increase the size of the objects of the class in memory (see [here](https://web.archive.org/web/20121208115058/http://www.parashift.com/c++-faq-lite/final-classes.html)). – user5534993 Jan 17 '22 at 15:16
  • Another reference for the virtual inheritance trick is Bjarne Stroustrup [here](https://web.archive.org/web/20111101181559/http://www2.research.att.com:80/~bs/bs_faq2.html#no-derivation). See also his book "The Design and Evolution of C++" sec 11.4.3. – user5534993 Jan 17 '22 at 15:17