简体   繁体   English

在C ++中使用继承时避免不需要的函数声明/定义

[英]Avoiding unrequired function declarations/definitions when using inheritance in C++

After a long time of C-style procedural coding, I am just beginning to 'get' OOP. 经过长时间的C风格程序编码,我才开始“获得”OOP。 So I suspect there may be standard way of dealing with the situation I am facing. 所以我怀疑可能有标准的方法来处理我所面临的情况。 I have an application with the 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 runs fine. 现在这个程序编译并运行正常。 However, I have question about its design. 但是,我对它的设计有疑问。 Class A is the common interface and does not need to be instantiated. A类是通用接口,不需要实例化。 Class B and C need to be. B类和C类需要。 Regarding the functions: intf() is needed by B but not C, and df() is needed by C but not B. If I make intf() {df()} pure virtual in A, then there is no reasonable definition of df() {intf()} for B {C}. 关于函数:B需要intf()而不是C,而C需要df()而不是B.如果我在A中使intf(){df()}纯虚,那么就没有合理的定义对于B {C},df(){intf()}。

Edit : B and C share some data members and also some member functions other than f(). 编辑B和C共享一些数据成员以及f()以外的一些成员函数。 I have not shown it my stripped down code. 我没有向我展示我的精简代码。

Finally, as is standard, my application needs to access both B and C through a pointer to A. So my question is: Is there a way to 'clean up' this design so that unrequired/empty member function definitions (such as I have done in declaration/definition of A) can be eliminated? 最后,作为标准,我的应用程序需要通过指向A的指针访问B和C.所以我的问题是:有没有办法'清理'这个设计,以便不需要/空的成员函数定义(比如我有在A)的声明/定义中完成可以消除吗? There is a clear "IS-A" relationship between the classes. 这些类之间存在明确的“IS-A”关系。 So even though I share every newbie's thrill about inheritance, I dont feel I have stretched my design just so I could use inheritance. 所以即使我分享每个新手对继承的兴奋,我也不觉得我已经延伸了我的设计,所以我可以使用继承。

Background in case it helps: I am implementing a regression suite. 背景以防万一:我正在实施一个回归套件。 Class A implements functions and matrices common to every regression (such as dependent and independent variables). A类实现每个回归共有的函数和矩阵(例如依赖变量和独立变量)。 Class B is logistic regression with two classes ('0' and '1') and defines cost functions, and training algorithm for two-class logistic regression. B类是逻辑回归,有两个类('0'和'1'),定义了成本函数,以及两类逻辑回归的训练算法。 Class C is multi-class logistic regression. C类是多类逻辑回归。 It extends class B by training for multiple classes using the "one-vs-all" algorithm. 它通过使用“one-vs-all”算法训练多个类来扩展B类。 So in a sense C is a binary logistic regression if you think of your class of interest as positive examples and all others as negative examples. 因此,在某种意义上,如果您认为您感兴趣的类作为正例,而所有其他作为反面例子,则C是二元逻辑回归。 Then you do it for every class to implement multi-class regression. 然后,您为每个类执行此操作以实现多类回归。 The functions (intf and df) in question return the output. 有问题的函数(intf和df)返回输出。 In case of logistic regression, the return value is a vector, while for multiclass regression, it is a matrix. 在逻辑回归的情况下,返回值是向量,而对于多类回归,它是矩阵。 And, as stated above, B and C dont have any use for each others' return functions. 并且,如上所述,B和C对彼此的返回功能没有任何用处。 Except that I cant seem to be able to eliminate redundant definitions in A (the regression class). 除了我似乎无法消除A(回归类)中的冗余定义。

Thanks for your help. 谢谢你的帮助。

Look at the Liskov Substitution Principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle). 请看Liskov替换原则(http://en.wikipedia.org/wiki/Liskov_substitution_principle)。 It states that subclasses must fulfill the same contract as the superclass. 它声明子类必须履行与超类相同的合同。 In your example, neither subclass does this. 在您的示例中,两个子类都不会这样做。 The "Is-A" relationship is not enough to justify inheritance. “Is-A”关系不足以证明继承的合理性。

One option would be to use a single 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 contract to be fulfilled by both subclasses while allowing the return type of the method to vary based on the subclass definition. 这将允许两个子类履行合同,同时允许方法的返回类型根据子类定义而变化。

If you want to learn more object oriented programming "best practices", google "Robert Martin SOLID" 如果你想学习更多面向对象的编程“最佳实践”,google“Robert Martin SOLID”

You touched one of the most controversial point of OOP: the is-a == derivation pattern, resulting int the "god object" anti-pattern, since everything is ultimately a child-of-god, with god knowing every method of everyone and having an "answer" (read "default implementation") for everything. 你触及了最有争议的OOP之一:is-a ==派生模式,导致了“上帝对象”的反模式,因为一切都是神童,上帝知道每个人的每一种方法对所有事情都有一个“答案”(读作“默认实施”)。

"Is-a" is not enough to justify inheritance, where no replace-ability exist, but in real world no object is really fully replaceable with another, otherwise it will not be different. “Is-a”不足以证明继承是合理的,在这种情况下不存在替换能力,但在现实世界中,任何对象都不能完全替换为另一个对象,否则它就不会有所不同。

You are in the "land of nowhere" where the substitution principle doesn't work well, but -at the same time- virtual functions look the best tool to implement dynamic dispatch. 您处于“无处之地”,替换原则不能很好地运行,但是 - 同时 - 虚函数看起来是实现动态调度的最佳工具。

The only thing you can do come to a compromise, and sacrifice one of the two. 你唯一可以做的就是达成妥协,并牺牲其中一个。

As far the situation looks like, since B and C have nothing in common (no shared useful methods), simply don't let those method to originate from A. If you have something to "share" is probably a runtime mechanism to discover the type of B or C before entering B related specific code or C related specific code. 到目前为止情况看来,由于B和C没有任何共同之处(没有共享的有用方法),所以根本不要让这些方法来自A.如果你有“分享”的东西可能是一个运行时机制来发现输入B相关特定代码或C相关特定代码之前的B或C类型。

This is typically done with a common base having a runtime-type indicator to switch upon, or just a virtual function (typically the destructor) to let dynamic_cast to be able to work. 这通常使用具有要运行的运行时类型指示符的公共库来完成,或者仅使用虚拟函数(通常是析构函数)来使dynamic_cast能够工作。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用C ++模块时,是否有任何理由将函数声明(.hpp文件)与其定义(.cpp文件)分开? - When using C++ modules, is the any reason to separate function declarations (.hpp files) from their definitions (.cpp files)? C ++:使用constexpr别名模板参数,导致函数定义与声明不匹配 - C++ : Using constexpr to alias template parameters, causing function definitions to not match to declarations 声明/定义为C和C ++中的语句 - Declarations/definitions as statements in C and C++ C++ 定义、声明和赋值到底是什么? - What exactly are C++ definitions, declarations and assignments? 避免 C++ 虚拟 inheritance - Avoiding C++ virtual inheritance 使用特征时,避免在部分模板特化中复制函数定义 - Avoiding duplication of function definitions in partial template specializations when using traits C ++函数声明 - C++ function declarations 在C ++中的类定义中避免循环依赖 - Avoiding Circular Dependencies in Class Definitions in C++ 使用C ++中相同的定义重载函数 - Overloading a function using identical definitions in C++ C ++构造函数,常量,继承和避免重复 - C++ Constructors, Constants, Inheritance and Avoiding Repetition
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM