0

Say I have an interface hierarchy :

class A
{
   virtual void commonFunc() = 0;
};

class B1 : public A
{
   virtual void b1SpecificFunc() = 0;
};

class B2 : public A
{
   virtual void b2SpecificFunc() = 0;
};

Interface A only exist to avoid duplicating the commonFunc() function.

Now if I want to implement this in order to have 2 instanciatable classes ImplB1 and ImplB2 i could do :

class ImplA
{
   virtual void commonFunc();
};

class ImplB1 : public ImplA
{
   virtual void b1SpecificFunc();
};

class ImplB2 : public ImplA
{
   virtual void b2SpecificFunc();
};

The problem with this is that it makes ImplA instanciatable, which I don't want to. I only want ImplB1 and ImplB2 to be instanciatable, because ImplA is something asbtract that only exist to have the implementation of the common function in common.

How could i design this please ? Thank you.

Virus721
  • 8,061
  • 12
  • 67
  • 123
  • 2
    Make ImplA's constructors protected and / or don't inherit from ImplA and use composition to share commonFunc's implementation. – Kristian Duske Mar 12 '15 at 16:59
  • 1
    @KristianDuske: Or just take `commonFunc` out of the entire class hierarchy. It's difficult to say without knowing more details. – Christian Hackl Mar 12 '15 at 17:21
  • Composition +1. Why not make constructors private but friends to Bs and instantiate it as a mebmer in Bs? – user3528438 Mar 12 '15 at 17:29

2 Answers2

2

Interface A only exist to avoid duplicating the commonFunc() function.

You certainly mean to avoid duplicating its declaration, don't you?

class ImplA
{
   virtual void commonFunc();
};

This should probably be:

class ImplA : public A
{
   virtual void commonFunc();
};

And I guess the point is that ImplA actually has an implementation of commonFunc. So for the sake of this answer's brevity, let's put it into the class definition:

class ImplA : public A
{
   virtual void commonFunc() {} // implementation
};

The problem with this is that it makes ImplA instanciatable.

Just make ImplA's destructor pure virtual:

class ImplA : public A
{
public:
   virtual ~ImplA() = 0 {}

private:
   virtual void commonFunc() {}
};

This will prevent instantiation even inside of derived classes' functions. For example, the following will create a compiler error:

class ImplB1 : public ImplA
{
public:
   virtual void b1SpecificFunc()
   {
       ImplA a; // error, cannot instantiate abstract class
   }
};

In fact, you will not even be able to instantiate the class in its own functions:

class ImplA : public A
{
public:
   virtual ~ImplA() = 0 {}

private:
   virtual void commonFunc()
   {
       ImplA a; // error, cannot instantiate abstract class
   }
};

But seriously, this all seems pretty over-engineered. Perhaps what you really need is to make commonFunc a non-virtual protected function of A, which derived classes can then call if they need to.

Or perhaps commonFunc can just be a free-standing utility function?

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 1
    virtual ~ImplA() = 0 {} what sorcery is this?! – Kristian Duske Mar 12 '15 at 17:23
  • Thanks for your help. Yeah it might seem a bit over complicated, but alll the solutions i thought about had something dirty. I'm not sure what you mean by free-standing utility functions, but this function is actually several functions common to both subclasses. – Virus721 Mar 12 '15 at 17:25
  • @KristianDuske: Well, it's a pure virtual destructor with an implementation. The destructor needs an implementation. At the end of the day, `= 0` really just means "the class of this member function is abstract", not "this function cannot be implemented". – Christian Hackl Mar 12 '15 at 17:25
  • 1
    @Virus721: It depends on what this `commonFunc` actually does. In C++, you don't have to put every function into a class. It's often easier (and *better* for a cleaner OOP approach!) to make functions completely free-standing. – Christian Hackl Mar 12 '15 at 17:26
  • Thanks for explaining, I wasn't aware that this was possible. – Kristian Duske Mar 12 '15 at 17:27
0

You can do the following. Also, here is a SO question/answer about this.

Note: While I'm answering the question that you can do this I'm not asserting it's what you should do.

Code

#include <iostream>

class A
{
public:
    virtual void commonFunc() = 0;
};


void A::commonFunc() // Pure virtual but implemented anyway
{
    // Derived classes can explicitly opt-in to this implementation
    std::cout << "A::commonFunc()\n";
}

class B1 : public A
{
public:
    virtual void b1SpecificFunc() = 0;
};

class B2 : public A
{
    virtual void b2SpecificFunc() = 0;
};

class ImplB1 : public B1
{
public:
    // This function must be implemented because its declared pure virtual
    virtual void commonFunc()
    {
        // Can override the behavior if desired...
        A::commonFunc(); // Explicitly use default implementation
    }

    virtual void b1SpecificFunc()
    {
        std::cout << "b1SpecificFunc()\n";
    }
};

class ImplB2 : public B2
{
public:
    // This function must be implemented because its declared pure virtual
    virtual void commonFunc()
    {
        // Can override the behavior if desired...
        A::commonFunc(); // Explicitly use default implementation
    }

    virtual void b2SpecificFunc()
    {
        std::cout << "b2SpecificFunc()\n";
    }
};

int main()
{
    //A a; // Won't compile (as expected/desired)
    ImplB1 b1;
    ImplB2 b2;

    b1.commonFunc();
    b1.b1SpecificFunc();

    b2.commonFunc();
    b2.b2SpecificFunc();

    return 0;
}

Output

A::commonFunc()
b1SpecificFunc()
A::commonFunc()
b2SpecificFunc()
Community
  • 1
  • 1
James Adkison
  • 9,412
  • 2
  • 29
  • 43