考虑像这样的一个类:
class MyReferenceClass
{
public:
MyReferenceClass();
const double ImportantConstant1;
const double ImportantConstant2;
const double ImportantConstant3;
private:
void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
}
有一个例程(ComputeImportantConstants)在运行时计算三个常量。假设计算相当复杂,并且固有地一次产生所有三个值。此外,结果取决于构建配置,因此不能选择对结果进行硬编码。
是否有一种合理的方法可以将这些计算值存储在类的相应const双字段中?
如果没有,你能建议一种更自然的方式在C ++中声明这样的一个类吗?
在C#中,我会在这里使用带静态构造函数的静态类,但这不是C ++中的一个选项。我也考虑过使用非const字段或函数调用使ImportantConstant1..3,但两者都显得逊色。
初始化我发现的const字段的唯一方法是use initializer lists,但似乎不可能在这样的列表中传递多输出计算的结果。
答案 0 :(得分:9)
你为什么不能这样做:
MyReferenceClass ComputeImportantConstants(){
//stuff to compute
return MyReferenceClass( const1, const2, const3 );
}
MyReferenceClass{
public:
MyReferenceClass(double _1, double _2, double _3)
: m_Const1(_1),
m_Const2(_2),
m_Const3(_3){}
double getImportantConst1() const { return m_Const1; }
double getImportantConst2() const { return m_Const2; }
double getImportantConst3() const { return m_Const3; }
private:
const double m_Const1,
m_Const2,
m_Const3;
};
就像这样,让计算功能变成工厂功能吗?
答案 1 :(得分:5)
首先 - 你可以做恶:在ComputeImportantConstants()中抛弃const并将值放在那里。不要这样做,因为那时你骗到编译器,它会试图找到最糟糕的回报方式。
第二
做这样的事情:
class A
private:
double important1;
double important2;
double important3;
A() { ComputeImportantConstants(); } //no need for parameters, it accesses the members
void ComputeImportantConstants();
public:
inline double GetImportant1() { return important1; }
inline double GetImportant2() { return important2; }
inline double GetImportant3() { return important3; }
};
你仍然可以通过使它成为某种单一左右来改进这个类(因为你只想进行一次计算)。
答案 2 :(得分:3)
您可以将const
字段移动到基类,然后传递一个包装类来初始化它们:
class MyBase
{
protected:
const double ImportantConstant1;
const double ImportantConstant2;
const double ImportantConstant3;
struct Initializer
{
double d1;
double d2;
double d3;
};
MyBase(Initializer const& i):
ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3)
{}
};
class MyReferenceClass:
private MyBase
{
public:
using MyBase::ImportantConstant1;
using MyBase::ImportantConstant2;
using MyBase::ImportantConstant3;
MyReferenceClass():
MyBase(makeInitializer())
{}
private:
MyBase::Initializer makeInitializer()
{
MyBase::Initializer i;
ComputeImportantConstants(&i.d1,&i.d2,&i.d3);
return i;
}
void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
};
答案 3 :(得分:2)
初始化我发现的const字段的唯一方法是使用初始化列表,但似乎不可能在这样的列表中传递多输出计算的结果。
这是真的;但是,您可以初始化单个成员 - 这是一个常量结构。见下文。
我还考虑过使用非const字段或函数调用使ImportantConstant1..3,但两者都显得逊色。
我不认为吸气功能会低劣。编译器很可能会内联它们。考虑一下:
class MyReferenceClass
{
public:
MyReferenceClass() : m_constants( ComputeImportantConstants() ) { }
inline double ImportantConstant1() const { return m_constants.c1; }
inline double ImportantConstant2() const { return m_constants.c2; }
inline double ImportantConstant3() const { return m_constants.c3; }
private:
struct Constants {
Constants( double c1_, double c2_, double c3_ ) : c1( c1_ ), c2( c2_ ), c3( c3_ ) { }
const double c1;
const double c2;
const double c3;
};
Constants ComputeImportantConstants() {
return Constants( 1.0, 2.0, 3.0 );
}
const Constants m_constants;
};
由于m_constants
及其所有字段都是常量,因此其他成员方法无法更改这些值 - 只需在您在问题中勾画的代码中。初始化可以是
在这里使用,因为我们初始化单个值:结构。
访问常量(很可能)与以前一样高效:建议内联函数,编译器很可能这样做,因为getter有多小。
答案 4 :(得分:2)
要修改已接受的答案,请注意,从C ++ 11开始,您可以做一些非常巧妙的技巧。例如,您的原始问题可以通过lambda和构造委托来解决,如下所示:
class MyReferenceClass {
public: /* Methods: */
MyReferenceClass()
: MyReferenceClass([](){
std::array<double, 3u> cs; /* Helper class, array or tuple */
computeImportantConstants(&cs[0u], &cs[1u], &cs[2u]);
return cs;
})
{}
const double importantConstant1;
const double importantConstant2;
const double importantConstant3;
private: /* Methods: */
MyReferenceClass(std::array<double, 3u> constants)
: ImportantConstant1(constants[0u])
, ImportantConstant2(constants[1u])
, ImportantConstant3(constants[2u])
{}
static void computeImportantConstants(double * out_const1,
double * out_const2,
double * out_const3);
}; /* class MyReferenceClass { */
或者更好的是,如果可能的话,将初始化代码从computeImportantConstants
移动到构造函数中的lambda中。
实际上,使用lambda调用初始化常量成员是一个非常方便的技巧,特别是因为您还可以绑定和/或传递参数到lambda。使用构造委托有助于简化成员的初始化,这些成员可以最好地一起初始化,也可以相互依赖。
但是,在使用构造委托时要特别小心,因为函数调用(或构造函数调用)的函数参数的初始化顺序是未定义的,并且可能最终以不正确的顺序或以某种方式初始化事物。如果出现故障或引发异常,可能导致资源泄漏。
答案 5 :(得分:1)
将事物拆分为易于初始化的部分和复杂部分,并通过复制构造函数初始化复杂部分:
// here's the part with the consts:
struct ComplexPart
{
const double a,b,c;
ComplexPart(double _a, double _b, double _c) {}
};
// here's the expensive calc function:
void calc(double *a,double *b,double *c);
// and this is a helper which returns an initialized ComplexPart from the computation:
ComplexPart calc2()
{
double *a,*b,*c;
calc(&a,&b,&b);
return ComplexPart(a,b,c);
}
// put everything together:
struct MyReferenceClass : public ComplexPart
{
MyReferenceClass() : ComplexPart(calc2()) {}
};
答案 6 :(得分:1)
这样的事情:
class A
{
private:
static void calc(double &d1, double &d2, double &d3)
{
d1 = 1.0;
d2 = 2.0;
d3 = 3.0;
}
class D
{
public:
operator double() const
{
return(x);
}
private:
friend class A;
double x;
};
public:
A()
{
calc(d1.x, d2.x, d3.x);
}
D d1, d2, d3;
};
#include <iostream>
int main()
{
A a;
std::cout << a.d1 << std::endl;
std::cout << a.d2 << std::endl;
std::cout << a.d3 << std::endl;
// the following lines will not compile so you can't change the value
// std::cout << a.d3.x << std::endl;
// a.d2.x = 0.0;
return(0);
}
答案 7 :(得分:1)
上面的答案似乎都没有注意到一个细节:这里提到了static
,所以这些常量似乎与类的实际实例无关。
换句话说:那些是全局常量。正如您所猜测的那样,const
关键字的存在在这里很重要,因为编译器将应用优化。
无论如何,我们的想法是使用辅助结构。
// foo.h
class Foo
{
public:
static double const m1;
static double const m2;
static double const m3;
};
// foo.cpp
struct Helper
{
double m1, m2, m3;
Helper() { complexInit(m1, m2, m3); }
} gHelper;
double const Foo::m1 = gHelper.m1;
double const Foo::m2 = gHelper.m2;
double const Foo::m3 = gHelper.m3;
当然,在一个真实的程序中,我会鼓励你实际上将常量包装在某种界面之后,以这种方式暴露它们是非常糟糕的做法,因为它使得更改它们(使用其他类型)非常困难。 / p>
另请注意,您不需要输出参数的指针,普通引用也可以。