1
votes

I want to use an abstract base class for the purpose of interfacing and usability. This abstract base class will be used as the parent class for several different derived classes, each of which has need of a certain subset of the pure virtual functions in the abstract base class. An outline of my scheme is as follows.

abstractclass.hpp

AbstractClass{
/* Declaration */
void pureVirt1() = 0;
void pureVirt2() = 0;
void pureVirt3() = 0;
};

derivedclass1.hpp

DerivedClass1{
/* Declaration */
void pureVirt1();
};

derivedclass2.hpp

DerivedClass2{
/* Declaration */
void pureVirt2();
};

derivedclass3.hpp

DerivedClass3{
/* Declaration */
void pureVirt3();
};

Given the current implementation, all of the above classes are abstract classes and no objects can be created from these classes. In the past, I have resolved this issue with preprocesser directives around each virtual function.

abstractclass.hpp

AbstractClass{
#ifdef _1
void purVirt1()=0;
#endif
...

And at the top of each derived class's header file, before the include to abstractclass.hpp I would write something like the following

derivedclass1.hpp

#define _1
#include"abstractclass.hpp"
...

This worked when I was working with small projects and not writing make files. The header for the abstract class was effectively altered based on which derived class was using it, so long as I kept my directives in the correct place. Now that I am using makefiles, this does not work. abstractcalss.cpp is compiled without any of the virtual functions in the header file because it is compiled separately from the derivedclass. I am looking for a good workaround for this.

I want this functionality because I have many similar derived classes from this abstract base class that are used by a variety of other tools I have written. I want to keep these other tools as simple as possible and just use pointers to the abstract class instead of writing template classes for everything.

--Further information I have a situation where AbstractClass is in a has-a relationship with SubAbstractClass and is implemented by use of having a pointer to SubAbstractClass in AbstractClass. Furthermore, for each of the derived classes there is a similar has-a relationship with SubDerivedClass1, SubDerivedClass2, … I don’t want to write containers for every new class that I create, especially because I can combine my derived classes to create novel classes that are important and functional and any such combination of new classes would require creating the appropriate set of subclasses. To this end, it is useful to have an ABC to allow the pointers to be declared once and work for any derived class.

2
I don't think this makes any sense at all. #1, the whole point of defining an ABC (abstract base class) is to declare methods that all derived classes implement, so they are interchangeable. #2, using the preprocessor to modify the ABC (and so end up with multiple conflicting definitions) results in undefined behaviour.Oliver Charlesworth
It does so currently yes, which is why I am looking for a better solution to this.PhiloEpisteme
If different derived classes to be derived from bases with different capabilities, then the bases should be different classes, not some mongrel hybrid that will, sooner or later, run afoul of the one-definition rule.Pete Becker
Agreed, this is somewhat of a perversion of the type system. Being an AbstractClass means nothing in your desired case -- it doesn't tell you anything about the actual functionality your objects support. What is the purpose of AbstractClass in your design?mwigdahl
@vckngs7: What problem are you trying to solve here? i.e. why do you want a common base class here?Oliver Charlesworth

2 Answers

1
votes

[...] several different derived classes, each of which has need of a certain subset of the pure virtual functions in the abstract base class.

Obviously, this won't work. Moreover, your attempts to make things simpler are, in my opinion, having the opposite effect. You are making things much more complex by introducing preprocessor black magic to comment-in and comment-out specific parts of the interfaces.

You're swimming upstream without a paddle here. Instead of having one interface class to which you add and remove methods piecemeal, just develop several different interface classes that do a better job of modularizing the functionality:

AbstractClass1{
/* Declaration */
void pureVirt1() = 0;
};

AbstractClass2{
/* Declaration */
void pureVirt2() = 0;
};

AbstractClass3{
/* Declaration */
void pureVirt3() = 0;
};

Trying to make one universal, God class that you blow pieces off of to suit specific modules's needs is going to eventually bite you, and hard. Consider what might happen when you need two instantiations of the interface in the same translation unit, but each of those instantiations have different pieced #defineed in. Sounds like a nightmare to me.

0
votes

Start with a common base class.

class AbstractBaseClass {
  // common stuff goes here
};

Then, create abstract interfaces for the sub-versions:

class AbstractSubClass1:public AbstractBaseClass {
public:
  void pureVirt1() = 0;
};
class AbstractSubClass2:public AbstractBaseClass {
public:
  void pureVirt2() = 0;
};
class AbstractSubClass3:public AbstractBaseClass {
public:
  void pureVirt3() = 0;
};

which contain the abstract pureVirt methods.

Finally, derive your implementation classes form the sub classes:

class Derived1 : public AbstractSubClass1 {
  virtual void pureVirt1() override; // override if your compiler supports it
};
class Derived2 : public AbstractSubClass2 {
  virtual void pureVirt2() override; // override if your compiler supports it
};
class Derived3 : public AbstractSubClass3 {
  virtual void pureVirt3() override; // override if your compiler supports it
};

now, objects that know you are a particular sub class can access the pureVirtX member. Those that don't only have access to the common AbstractBaseClass interface (whatever that is - you mention that some code doesn't need to know about these particular virtual methods).