1

我最近写了一个渲染 B 样条曲线的类。这些曲线由多个控制点定义。最初,我打算使用八个控制点,所以我在类中添加了一个常量,如下所示:

class Curve
{
   public:
      static const int CONTROL_POINT_COUNT = 8;
};

现在我想扩展这个类以允许任意数量的控制点。所以我想把它改成:

class Curve
{
   public:
      int getControlPointCount() {return _controlPointCount;}
};

问题是在方法中存储常量是否更好,以促进适应性。换句话说,这样开始不是更好吗:

class Curve
{
   public:
      int getControlPointCount() {return 8;}
};

这样做的好处是我可以只更改相关方法中的一个符号,而不是移动常量等。

这是一个好习惯还是坏习惯?

4

9 回答 9

2
int getControlPointCount() {return _controlPointCount;}

这是一个访问器。正如 litb 所指出的那样,为访问器交换 const static 并不是真正的收获。你真正需要可能是一对访问器和修改器。

int getControlPointCount() {return _controlPointCount;} // accessor

我还会为访问器添加一个 design-const 并制作它:

int getControlPointCount() const {return _controlPointCount;} // accessor

和相应的:

void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator

现在,与静态对象的最大区别在于控制点计数不再是类级别的属性,而是实例级别的属性。这是设计上的改变。你想要这样吗?

Nit:您的类级别静态计数是public并且因此不需要访问器。

于 2009-03-15T11:30:04.853 回答
1

为了更好地回答您的问题,还应该知道 controlPointCount 变量的设置方式。它是在您的班级之外设置的吗?在这种情况下,您还应该定义一个 setter。还是 Curve 类是唯一负责设置它的?它是仅在编译时设置还是在运行时设置。

无论如何,即使是这种形式,也要避免使用幻数:

int getControlPointCount() {return 8;}

这个更好:

int getControlPointCount() {return CONTROL_POINT_COUNT;}

方法的优点是您可以修改内部实现(使用常量值,从配置文件中读取,动态更改值),而不会影响类的外部。

于 2009-03-15T11:28:14.030 回答
1

通常,我倾向于手动维护尽可能少的联轴器。

曲线中控制点的数量就是曲线中控制点的数量。它不是一个可以随意设置的自变量。

所以我通常会公开一个 const 标准容器引用:

class Curve
{   
    private:
        std::vector<Point>& _controlPoints;

    public:      
        Curve ( const std::vector<Point>& controlPoints) : _controlPoints(controlPoints)
        {
        }

        const std::vector<Point>& getControlPoints ()
        {
            return _controlPoints;
        }
};

如果您想知道有多少控制点,请使用curve.getControlPoints().size(). 我怀疑在大多数用例中,无论如何您都需要点数和计数,并且通过公开标准容器,您可以使用标准库的迭代器习惯用法和内置算法,而不是获取计数和调用像getControlPointWithIndex循环一样的函数。

如果曲线类中真的没有其他东西,我什至可以做到:

typedef std::vector<Point> Curve;

(通常曲线不会自己渲染,因为渲染器类可以包含有关渲染管道的详细信息,从而使曲线纯粹是几何工件)

于 2009-03-15T12:04:38.000 回答
0

通常,您的所有数据都应该是私有的,并且可以通过 getter 和 setter 访问。否则你违反封装。也就是说,如果您公开底层数据,您将自己和您的班级锁定在该底层数据的特定表示中。

在这种特定情况下,我会做如下我认为的事情:

class Curve
{

   protected:

      int getControlPointCount() {return _controlPointCount;}
      int setControlPointCount(int c) { _controlPointCount = c; }

   private:

      static int _controlPointCount = 0;
};
于 2009-03-15T11:33:54.233 回答
0
class Curve
{   
    private:
        int _controlPointCount;

        void setControlPointCount(int cpc_arg)
        {
            _controlPointCount = cpc_arg;
        }

    public:      
        curve()
        {
            _controlPointCount = 8;
        }

        int getControlPointCount() const
        {
            return _controlPointCount;
        }
};

我将创建一个这样的代码,使用私有的 set 函数,这样任何主体都无法使用控制点计数,直到我们进入下一阶段的开发......我们更新开始在运行时更新控制点计数。那时,我们可以将这个 set 方法从私有范围移动到公共范围。

于 2009-03-15T11:34:52.173 回答
0

在理解这个问题的同时,我对这个例子有一些概念上的问题:

  • getControlPointCount()当控制点的数量不受限制时, 返回值是多少?
    • 是MAXINT吗?
    • 它是曲线上当前控制点的数量(从而打破了说这是最大可能点数的逻辑吗?)
  • 当您实际尝试使用 MAXINT 点创建曲线时会发生什么?你最终会耗尽内存。

界面本身对我来说似乎有问题。与其他标准集合类一样,该类应该封装其对点数的限制,并且AddControlPoint()如果发生大小、内存限制或任何其他违规行为,它应该返回错误。

至于具体的答案,我同意kgiannakakis:成员函数允许更大的灵活性。

于 2009-03-15T12:15:04.463 回答
0

我倾向于通过执行程序对所有“稳定”值使用配置+常量(默认值)。对于无法更改的值(360 度 -> 2 pi 弧度,60 秒 -> 1 分钟)或其更改会破坏正在运行的代码(使其不稳定的算法的最小/最大值)的普通常量。

您正在处理一些不同的设计问题。首先您必须知道控制点的数量是类还是实例级别的值。然后它是否是两个级别中的任何一个级别的常数。

如果所有曲线必须在您的应用程序中共享相同数量的控制点,则它是类级别(静态)值。如果不同的曲线可以有不同数量的控制点,那么它不是类级别的值,而是实例级别的值。

在这种情况下,如果控制点的数量在曲线的整个生命周期内保持不变,那么它就是实例级常数,如果它可以改变,那么它在这个水平上也不是常数。

// Assuming that different curves can have different 
// number of control points, but that the value cannot 
// change dynamically for a curve.
class Curve
{
public:
   explicit Curve( int control_points )
      : control_points_( control_points )
   {}
   // ...
private:
   const int control_points_;
};

namespace constant
{
   const int spline_control_points = 8;
}
class Config
{
public:
   Config();
   void readFile( std::string const & file );

   // returns the configured value for SplineControlPoints or
   // constant::spline_control_points if the option does not 
   // appear in config.
   int getSplineControlPoints() const;
};

int main()
{
   Config config;
   config.readFile( "~/.configuration" ); // read config

   Curve c( config.getSplineControlPoints() );
}
于 2009-03-15T12:41:24.137 回答
0

对于整数类型,我通常使用:

class Curve
{
   public:
      enum 
      {
          CONTROL_POINT_COUNT = 8
      };
};

如果除了类实现之外的任何实体都不需要常量,我在 *.cpp 文件中声明常量。

namespace
{
const int CONTROL_POINT_COUNT = 8;
}
于 2009-03-15T12:54:09.893 回答
0

一般来说,常量不应该在方法内部定义。您选择的示例有两个独特的功能。首先,它是一个吸气剂;其次,返回的类型是 int。但是定义常量的目的是不止一次地使用它们,并且能够以方便的方式引用它们。键入“8”而不是“controlPointCount”可能会节省您的时间,并且似乎不会产生维护成本,但如果您总是在方法中定义常量,这通常不会是真的。

于 2009-03-16T12:22:00.787 回答