Avoiding unnecessary declarations / function definitions when using inheritance in C ++

After a long time of procedural C-style coding, I am just starting to โ€œreceiveโ€ OOP. Therefore, I suspect that there may be a standard way to deal with the situation I am facing. I have an application with a class hierarchy shown below:

#include <iostream> using namespace std; class A { public: virtual int intf() { return 0;} // Only needed by B virtual double df() {return 0.0;} // Only needed by C }; class B : public A { int intf() {return 2;} // B objects have no use for df() }; class C : public B { double df() {return 3.14;} // C objects have no use for intf() }; int main(){ // Main needs to instantiate both B and C. B b; C c; A* pa2b = &b; A* pa2c = &c; cout << pa2b->intf() << endl; cout << pa2b->df() << endl; cout << pa2c->intf() << endl; cout << pa2c->df() << endl; return 0; } 

Now this program compiles and works fine. However, I have a question about its design. Class A is a common interface and does not require instantiation. Class B and C should be. As for the functions: intf () requires B, but not C, and df () requires C, not B. If I make intf () {df ()} purely virtual in A, then there is no reasonable definition of df () {intf ()} for B {C}.

Edit : B and C share some data elements, as well as some member functions other than f (). I did not show this code.

Finally, as standard, my application needs to access both B and C using a pointer to A. So my question is: is there a way to "clear" this construct so that I can define unrequired / empty member function as I did in declaration / definition A) can be eliminated? Between classes there is a clear relationship "IS-A". Therefore, despite the fact that I am talking about the whole novelty of inheritance, I do not feel that I have stretched my design to use inheritance.

Background information in case this helps: I am implementing a regression set. Class A implements functions and matrices common to each regression (such as dependent and independent variables). Class B is a logistic regression with two classes ("0" and "1") and defines cost functions, as well as a learning algorithm for two-class logistic regression. Class C - multiclass logistic regression. He extends class B by teaching for several classes using the one-all-all algorithm. Thus, in a sense, C is a binary logistic regression if you think that your interest class is a positive example and all the others are negative. Then you do this for each class to implement multiclass regression. Implemented functions (intf and df) return the result. In the case of logistic regression, the return value is a vector, and for multiclass regression, it is a matrix. And, as stated above, B and C have no use for each other's returned functions. Except that I can't seem to eliminate redundant definitions in (regression class).

Thank you for your help.

+4
source share
2 answers

You touched on one of the most controversial points of OOP: the is-a == output template, as a result of which the anti-divine object template was created, since everything is ultimately a divine child, with a god who knows every method of all and has an โ€œanswer "(read" default implementation ") for everything.

"Is-a" is not enough to justify inheritance, where there is no possibility of replacement, but in the real world, no object can be completely replaced by another, otherwise it will not be different.

You are in the "land of nowhere", where the principle of substitution does not work well, but at the same time, virtual functions look like the best tool for implementing dynamic sending.

The only thing you can do is compromise and sacrifice one of two things.

As far as the situation looks, since B and C have nothing in common (there are no common useful methods), just do not let this method arise from A. If you have something that can be "split up", it is probably a runtime mechanism to detect type B or C before entering a specific code associated with B or a code associated with C.

This is usually done with a common base in which a runtime type indicator is turned on, or simply a virtual function (usually a destructor) to allow dynamic_cast to work.

 class A { public: virtual ~A() {} template<class T> T* is() { return dynamic_cast<T*>(this); } }; class B: public A { public: int intf() { return 2; } }; class C: public A { public: double df() { return 3.14; } }; int main() { using namespace std; B b; C c; A* ba = &b; A* ca = &c; B* pb = ba->is<B>(); if(pb) cout << pb->intf() << endl; C* pc = ca->is<C>(); if(pc) cout << pc->df() << endl; } 
+3
source

Take a look at the Liskov Substitution Principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle). It states that subclasses must fulfill the same contract as the superclass. In your example, none of the subclasses does this. The Is-A ratio is not enough to justify inheritance.

One option is to use one template method something like this:

 template <typename T> class A<T> { T getValue(); } class B : A<int> { int getValue(); } class C: A<double> { double getValue(); } 

this would allow the execution of a contract for both subclasses, allowing the type of the return method to vary depending on the definition of the subclass.

If you want to learn more about object-oriented programming "Best Practices", Google "Robert Martin SOLID"

+5
source

Source: https://habr.com/ru/post/1442396/


All Articles