C ++是否可以不继承成员?

时间:2016-12-27 11:54:44

标签: c++ inheritance private member

我很确定不是,但无论如何我都要问...... 这是一个例子: 假设我有一个名为Elipse的课程:

class Elipse
{
private:
  double d_m_lAxis; // the length of the large Axis of the elipse
  double d_m_sAxis; // short axis
//Point is a struct of 2 doubles (x and y)
  Point P_m_focal1; //focal point 1
  Point P_m_focal2; //focal point 2
protected:
  Point P_m_center; 

public:
  Elipse(Point _P_lowerLeft,Point _L_upperRight)
  {
    //draw elipse in a rectangle, just like in paint
  }
}

现在我想从Circle继承Elipse(因为Circle是一个ElipseAxis的长度相同{/ p>}:

class Circle : public Elipse   
{
private:
  double d_m_radius;
public:
  Circle(Point _P_lowerLeft,double _d_diameter) :
    Elipse(_P_lowerLeft,/*get the upper ight*/
      Point(_P_lowerLeft.x + _d_diameter,_P_lowerLeft.y + _d_diameter))
      {
        // draw a circle in a square, just like in paint
      }
}

所以现在我得到了我想要的课程,并且它无权访问Elipse中任何Circle的私人成员。Elipse。 但是,据我所知,这些成员仍然继承,而子类根本无法访问它们。这使得该类的大小不必要地大,并且还可以通过从{{1}}继承的函数来访问这些成员,除非它们被覆盖。

有没有办法继承成员?

5 个答案:

答案 0 :(得分:3)

你试图通过这种继承来实现的目标违反了Liskov Substitution原则。实际上在你的情况下,Ellipse不应该是Circle的基础。查看更多细节,在维基百科上:

https://en.wikipedia.org/wiki/Liskov_substitution_principle#A_typical_violation - 这里基于非常相似的例子(矩形和方形)来解释

答案 1 :(得分:3)

  

这会让课程大小不必要 ,而且它也是   可以通过从Elipse继承的函数访问这些成员,   除非他们被覆盖。有没有办法不继承成员   所有?

虽然,您可能想重新考虑您的设计, 你可以利用 EBO(空基类优化),没有大小开销。

有一个空类型,如:

class EllipticalType {};

然后定义你的椭圆

class Elipse : public EllipticalType
{
private:
  double d_m_lAxis; // the length of the large Axis of the elipse
  double d_m_sAxis; // short axis
//Point is a struct of 2 doubles (x and y)
  Point P_m_focal1; //focal point 1
  Point P_m_focal2; //focal point 2
protected:
  Point P_m_center; 

public:
  Elipse(Point _P_lowerLeft,Point _L_upperRight)
  {
    //draw elipse in a rectangle, just like in paint
  }
}

然后你做:

class Circle : public EllipticalType
{
          private:
            double d_m_radius;
          public:
    Circle(Point _P_lowerLeft,double _d_diameter) : Elipse(_P_lowerLeft,/*get the upper right*/Point(_P_lowerLeft.x + _d_diameter,_P_lowerLeft.y + _d_diameter))
        {
        // draw a circle in a square, just like in paint
        }

}

您还可以提取普通成员并将其放入EllipticalType

答案 2 :(得分:2)

没有。你不能。 C ++中的继承意味着在实践中具有一定程度的二进制兼容性。

接下来是一种解决方法。但首先:

圆圈不是椭圆。

更确切地说,可变圆圈在许多有用的方面不是可变椭圆。您可以对可变椭圆执行操作,其后置条件与可变圆上的相同操作不对齐。

例如,假设有get / set主轴/副轴。设定长轴的合理后置条件是得到短轴在可变椭圆上保持不变。虽然您可以在圆上设置长轴/短轴,但该后置条件无法保持!

现在,一个不可变的圆是一个不可变的椭圆。只有一旦你开始编辑它就会发生协变/逆变问题。这个问题不仅限于矩形/正方形,椭圆形/圆形,汽车/卡车式和#34;玩具" OO; Derived的可编辑列表不是以相同的方式编辑Base的列表。可编辑的Base列表接受任何 Base类型; Derived列表也是如此。

另一方面,Derived的一个不可变列表是Base的不可变列表。

回到原来的问题,继承这样的实现可能是不明智的。但是,您可以从接口的逻辑中分割数据。

然后继承接口和逻辑,同时保持数据不相关。

在C ++中,这可以通过模板和CRTP完成。如果您不了解CRTP,以下内容将是神秘且不可读的。我不保证计数。

template<class Self>
struct ellipse_math {
  Self* self(){ return static_cast<Self*>(this);}
  Self const* self() const { return static_cast<Self const*>(this);}

  double get_area() const { /* math using self()->get_major() and self()->get_minor(); */ }
};
struct circle_state {
  double m_radius;
  double get_major() const;
  double get_minor() const;
  double get_radius() const;
};
struct ellipse_state {
  double m_major, m_minor;
  double get_major() const;
  double get_minor() const;
};
struct I_ellipse {
  virtual double major()const=0;
  virtual double minor()const=0;
  cirtual double area()const=0;
  virtual ~I_ellipse(){};
};
struct I_circle:I_ellipse {
  virtual double radius()const=0;
};
template<class Self, class I=I_ellipse>
struct impl_I_ellipse : I  {
  Self* self(){ return static_cast<Self*>(this);}
  Self const* self() const { return static_cast<Self const*>(this);}
  virtual double major()const final override { return self()->get_major(); }
  virtual double minor()const final override { return self()->get_minor(); }
  virtual double area()const final override { return self()->get_area(); }
};
template<class Self, class I=I_circle>
struct impl_I_circle : impl_I_ellipse<Self, I> {
  Self* self(){ return static_cast<Self*>(this);}
  Self const* self() const { return static_cast<Self const*>(this);}
  virtual double radius()const final override { return self()->get_radius(); }
};

struct ellipse :
  ellipse_state,
  impl_I_ellpise<ellipse>,
  ellpise_math<ellipse>
{};
struct circle  :
  circle_state,
  impl_I_circle<circle>,
  ellpise_math<circle>
{};

在这个简单的例子中,这非常荒谬,但我确实避免在我的圈子实现中使用未使用的椭圆字段,并且重新使用了ellpise逻辑。

以上是在手机上输入的,所以可能包含tpyos。

老实说,如果我走得这么远,我会一直使用自由函数进行调度,一直到SBO类型擦除椭圆状和类似圆形的物体。因为上面的继承和vtable比他们的帮助更多。

答案 3 :(得分:2)

数学短语“一个圆圈是一个椭圆,其中[...]”应该被重新描述为更多类型的“对于每个圆圈,都存在一个椭圆,其中[... 。],描述了同一组点“

考虑到这一点,您的实现应该允许从public class MainActivity extends AppCompatActivity { public final static String EXTRA_MESSAGE = "com.my.application.message"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void sendMessage(View view){ Intent intent = new Intent( this , DisplayMessageActivity.class); EditText editText; editText = (EditText) findViewById(R.id.edit_message); //here System.out.println("the values is "+ editText); String message = editText.getText().toString(); intent.putExtra(EXTRA_MESSAGE,message); startActivity(intent); } } 个对象创建Ellipse个对象,而不是多态替换。
其中一种可能性是显式转换运算符:

Circle

答案 4 :(得分:1)

圆是elipse的特例,不是它的子类型,所以你不需要这样做。相反,只需为任何轴初始化具有相同值的elipse。

关于继承,私人,受保护的成员就是为此而存在的。他们喜欢将继承的私人。由于基类私有成员,该类不会很大。编译器将优化最终代码。