我很确定不是,但无论如何我都要问......
这是一个例子:
假设我有一个名为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
是一个Elipse
,Axis
的长度相同{/ 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}}继承的函数来访问这些成员,除非它们被覆盖。
有没有办法不继承成员?
答案 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。
关于继承,私人,受保护的成员就是为此而存在的。他们喜欢将继承的私人。由于基类私有成员,该类不会很大。编译器将优化最终代码。