我最近写了一个渲染B样条曲线的类。这些曲线由许多控制点定义。最初,我打算使用8个控制点,所以我在课堂上添加了一个常量,如下所示:
class Curve
{
public:
static const int CONTROL_POINT_COUNT = 8;
};
现在我想扩展这个类以允许任意数量的控制点。所以我想把它改成:
class Curve
{
public:
int getControlPointCount() {return _controlPointCount;}
};
问题在于,在方法中存储常数是否更好,以促进适应性。换句话说,这样开始不是更好:
class Curve
{
public:
int getControlPointCount() {return 8;}
};
这样做的好处是我可以在相关方法中更改一个符号,而不是移动常量等。
这是一个好习惯还是坏习惯?
答案 0 :(得分:2)
int getControlPointCount() {return _controlPointCount;}
这是一个访问者。像litb指出的那样,为访问器交换const静态并不是真正的好处。您真正需要面向未来的可能是一对访问者和变异者。
int getControlPointCount() {return _controlPointCount;} // accessor
我还会为访问者提供一个design-const并将其设为:
int getControlPointCount() const {return _controlPointCount;} // accessor
和相应的:
void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator
现在,与静态对象的最大区别在于控制点计数不再是类级别属性,而是实例级别1。这是设计更改。你想要这样吗?
Nit:您的班级静态计数为public
,因此不需要访问者。
答案 1 :(得分:1)
为了更好地回答您的问题,还应该知道如何设置controlPointCount变量。它是否在您的课堂之外?在这种情况下,您还应该定义一个setter。或者曲线类是唯一负责设置的?它是仅在编译时还是在运行时设置的。
无论如何,即使以这种形式避免使用幻数:
int getControlPointCount() {return 8;}
这样更好:
int getControlPointCount() {return CONTROL_POINT_COUNT;}
方法的优点是您可以修改内部实现(使用常量值,从配置文件中读取,动态更改值),而不会影响类的外部。
答案 2 :(得分: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;
(通常曲线不会渲染自身,因为渲染器类可以包含有关渲染管道的详细信息,将曲线保留为纯粹的几何图形)
答案 3 :(得分:0)
通常,您的所有数据都应该是私有的,并通过getter和setter访问。否则你违反了封装。也就是说,如果您公开基础数据,则将您自己和您的类锁定为该基础数据的特定表示。
在这个特定的情况下,我会做以下的事情,我想:
class Curve
{
protected:
int getControlPointCount() {return _controlPointCount;}
int setControlPointCount(int c) { _controlPointCount = c; }
private:
static int _controlPointCount = 0;
};
答案 4 :(得分: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方法从私有范围移到公共范围。
答案 5 :(得分:0)
在理解这个问题的同时,我对这个例子有很多概念上的问题:
getControlPointCount()
的返回值是多少?
界面本身对我来说似乎有问题。与其他标准集合类一样,该类应该封装其对点数的限制,如果发生了对大小,内存或任何其他违规的限制,则其AddControlPoint()
应该返回错误。
至于具体答案,我同意kgiannakakis:成员函数允许更多的灵活性。
答案 6 :(得分:0)
我倾向于通过执行程序对所有“稳定”值使用配置+常量(默认值)。使用无法更改的值的普通常量(360度 - > 2 pi弧度,60秒 - > 1分钟)或其更改将破坏正在运行的代码(使算法不稳定的算法的最小/最大值)。
您正在处理一些不同的设计问题。首先,您必须知道控制点的数量是类级别还是实例级别值。然后它是否是两个级别中的任何一个的常量。
如果所有曲线必须在应用程序中共享相同数量的控制点,那么它是类级别(静态)值。如果不同的曲线可以具有不同数量的控制点,那么它不是类级别值,而是实例级别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() );
}
答案 7 :(得分:0)
对于整数类型我通常使用:
class Curve
{
public:
enum
{
CONTROL_POINT_COUNT = 8
};
};
如果常量不需要除类实现之外的任何实体,我在* .cpp文件中声明常量。
namespace
{
const int CONTROL_POINT_COUNT = 8;
}
答案 8 :(得分:0)
通常不应在方法内定义常量。您选择的示例有两个独特的功能。首先,它是一个吸气剂;第二,返回的类型是int。但是定义常量的关键是不止一次地使用它们,并且能够以方便的方式引用它们。键入“8”而不是“controlPointCount”可能会节省您的时间,并且可能不会产生维护成本,但如果始终在方法中定义常量,则通常不会这样。