Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/131.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 基类应该有属性吗?_C++_Oop - Fatal编程技术网

C++ 基类应该有属性吗?

C++ 基类应该有属性吗?,c++,oop,C++,Oop,在另一个社区,我有人建议在基类中“永远”不要有属性。相反,它应该只有纯虚函数,并让派生类负责为所有这些方法提供定义 我的看法是“这不是经验法则”。我举了一个简单的例子: class Shape { protected: double Area; double Perimeter ; public: Shape():Area(0.0), Perimeter(0.0){} double getArea(){return

在另一个社区,我有人建议在基类中“永远”不要有属性。相反,它应该只有纯虚函数,并让派生类负责为所有这些方法提供定义

我的看法是“这不是经验法则”。我举了一个简单的例子:

class Shape
{
    protected:
        double Area;
        double Perimeter ;
    public:
        Shape():Area(0.0), Perimeter(0.0){}
        double getArea(){return Area;}
        double getPerimeter (){return Perimeter;}
        virtual void setArea () = 0 ;
        virtual void setPerimeter () = 0 ;
};

class Rectangle : public Shape
{
    private :
        double length ;
        double breadth ;
    public:
        Rectangle ():length(0.0),breadth(0.0){}
        Rectangle (int _length, int _breadth):length(_length),breadth(_breadth){}
        void setArea ()
        {
            Area = length * breadth ;
        }
        void setPerimeter ()
        {
            Perimeter = 2 * (length + breadth) ;
        }
} ; 
在上面给出的示例中,我觉得任何形状都有以下属性'Area''permiture',因此,如果我们不在类中提供这些属性,那么形状类将不会是真实世界形状的适当表示形式

请让我知道你对此的看法

编辑:

我要说的第一件事是,我的代码确实很幼稚,没有太多的意义。我之前想我会加一句话,说这只是一个例子,这篇文章的重点是要知道“基类是否应该永远不应该有属性”。但后来我想我也会得到一些好的设计建议,我确实得到了很多:)

说到这个问题,在下面的所有帖子中,我明白这不是经验法则,而是设计选择(这正是我想要强调的)。话虽如此,我也承认,如果这些属性可以作为面积=长度*宽度来计算(或派生),那么最好不要在基类中包含属性。
非常感谢您的所有答案(我希望我可以选择接受多个答案)。

在基类中包含变量是完全可以接受的。但是,许多语言不允许这样做,因此基类只能具有虚拟函数。这可能就是你听到这个的原因


在这个问题上有很多不同的观点,但是在C++中,我认为基类存储数据是很常见的。

< P>在基类中没有数据会要求派生类中不相同的代码重复。防止代码重复是面向对象设计内置的概念之一。

回答您的问题。。。。是的,在基类中使用适用于所有子类的属性。例如,如果您知道形状(多边形!)始终具有长度和宽度,请将它们用作基类的属性

我不明白为什么要为创建的形状(多边形)的每个实现重新创建这些


…我想如果你可以将圆的周长称为周长,你可以使用shape类。

我认为这个建议来自于风格问题。将一种协议(接口)类作为只发布类的方法的基础被认为是更干净的。实现应该尽可能对类客户机隐藏。如果还将数据存储在基类中,则实现类不能自由选择存储数据的最佳方式。这样基类就限制了实现的可能性。也许有一个实现想要计算一个allready存储值,这样就不需要空间,但是Interitor无法释放(非动态)分配的内存


因此,作为一份简历,我想说,这取决于你是否“只编写”了一个东西,或者你是否想创建一个庞大的库来进行10年的维护,这需要一种更干净的方法。

我不同意你的例子。当然,一个矩形有一个面积和一个周长,当你可以在getter中动态计算它们时,将它们与长度和宽度分开存储是没有多大意义的。既然可以提供更好的界面,为什么还要强迫人们调用这些“Set”函数来获得准确的结果呢

我会使
形状
成为一个纯粹的界面:

struct Shape
{
    virtual ~Shape(); // probably need this
    virtual double getArea () = 0 ;
    virtual double getPerimeter () = 0 ;
};

class Rectangle : public Shape
{
    double length ;
    double breadth ;
public:
    Rectangle ():length(0.0),breadth(0.0){}
    Rectangle (int _length, int _breadth):length(_length),breadth(_breadth){}
    double getArea ()
    {
        return length * breadth ;
    }
    double getPerimeter ()
    {
        return 2 * (length + breadth) ;
    }
};
我觉得任何形状都有以下属性“面积”和“周长”,因此如果我们不在类中提供这些属性,则形状类将不是“真实世界形状”的适当表示

我也不同意这一点。Shape类应该有一个公共API,提供对其区域和周长的访问。它是否有包含这些值的字段是一个实现细节,与它是否向外部世界表示形状无关

比如说,为外部世界提供一个好的接口是类设计的85%。另外15%是确保适合调用方的接口实际上是可行的。使内部实现细节遵循与外部接口相同的模式,以至于函数返回的对象的每个属性都必须存储在字段中,这不仅是低优先级的,而且是毫无意义的

如果你有一个圆,你可能会想到
getRadius
getDiameter
函数,但是你不会将它们存储在不同的字段中,尽管事实上圆当然有半径和直径

现在,如果这不是一个矩形,而是一个计算面积和周长很慢的非常复杂的形状,那么您可能希望在对象中缓存面积和周长(并且在定义参数更改时重新计算它们或将缓存标记为过时)。但这是复杂形状的一个特殊属性,与矩形等简单形状无关,因此不属于基类形状。基类应该只包含对所有派生类通用的东西,或者至少对足够多的派生类通用,这样您就可以像对所有派生类通用一样(并覆盖或忽略它们不适用的子类中的那些东西)。在您的示例中,报告面积和周长的功能对于所有形状都是通用的。在数据成员中存储面积和周长的事实不需要对所有形状都通用,在矩形的情况下,它会占用额外的内存并需要额外的内存
class shape
{
    public:
        ~shape(){}

        virtual double area(void) const = 0;
        virtual double perimeter(void) const = 0;
};
class square : public shape
{
public:
    square(double pSide) : mSide
    {}

    double area(void) const
    {
        return mSide * mSide;
    }

    double perimeter(void) const
    {
        return 4 * mSide;
    }

private:
    double mSide;
};
template <typename Shape>
class shape_attribute_cache : public shape
{
public:
    // provide shape interface
    double area(void) const
    {
        return cached_area();
    }

    double perimeter(void) const
    {
        return cached_perimeter();
    }

protected:
    shape_attribute_cache() : mArea(0), mPerim(0), mChanged(false) {} 
    ~shape_attribute_cache(){} // not to be used publically 

    double cached_area(void) const { update_area(); return mArea; }
    void cached_area(double pArea) { mArea = pArea; }

    double cached_perimeter(void) const { update_perimeter(); return mPerim; }
    void cached_perimeter(double pPerim) { mPerim = pPerim; }

    void changed(void) { mAreaChanged = true; mPerimChanged = true; }

private:
    void update_area(void)
    {
        if (!mAreaChanged) return;

        // delegate to derived shapes update method
        static_cast<Shape*>(this)->update_area();
        mAreaChanged = false;
    }

   void update_perimeter(void)
    {
        if (!mPerimChanged) return;

        // delegate to derived shapes update method
        static_cast<Shape*>(this)->update_perimeter();
        mPerimChanged = false;
    }

    double mArea;
    double mPerim;
    bool mAreaChanged;
    bool mPerimChanged;
};

// use helper implementation
class expensive_shape : public shape_attribute_cache<expensive_shape>
{
public:
    // ...

    void some_attribute(double pX)
    {
        mX = pX;
        changed(); // flag cache is bad, no longer callers responsibility
    }

private:
    // ...

    void update_area(void)
    {
        double newArea = /* complicated formula */;
        cached_area(newArea);
    }

    void update_perimeter(void)
    {
        double newPerim = /* complicated formula */;
        cached_perimeter(newPerim);
    }
}; 
class Shape
{
    private:
        double Area;
        double Perimeter ;
    public:
        Shape(double the_area, double the_perimeter): Area(the_area), Perimeter(the_perimeter){}
        double getArea() const {return Area;}
        double getPerimeter () const {return Perimeter;}
};

class Rectangle : public Shape
{
    private :
        double length ;
        double breadth ;
    public:
        Rectangle (int _length, int _breadth):Shape(length * breadth, 2 * (length + breadth)), length(_length),breadth(_breadth){}
} ; 
class base {
public:
  base(int x);
  virtual ~base();

  inline int x() const { return x_; }

  // virtual interface declared here

private:
  int x_;
};

class derived : public base { ... };
class base {
public:
  virtual ~base();
  virtual int x() const = 0;

  // ...
};

class base_with_static_x : public base {
public:
  base_with_static_x(int);
  virtual ~base_with_static_x();

  inline int x() const { return x_; }

  // ...

private:
  int x_;
};

class derived : public base_with_static_x { ... };