12

考虑这样一个类:

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++ 中的选项。我还考虑过使 ImportantConstant1..3 成为非常量字段或函数调用,但两者似乎都逊色。

我发现初始化 const 字段的唯一方法是使用初始化列表,但似乎无法在这样的列表中传递多输出计算的结果。

4

8 回答 8

10

为什么你不能这样做:

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;
};

像那样让计算函数变成工厂函数吗?

于 2010-07-15T14:01:36.290 回答
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; }
};

您仍然可以通过使其成为某种单例左右来改进此类(因为您希望计算只进行一次)。

于 2010-07-15T14:04:57.793 回答
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);
};
于 2010-07-15T14:21:50.837 回答
2

我发现初始化 const 字段的唯一方法是使用初始化列表,但似乎不可能在这样的列表中传递多输出计算的结果。

确实如此; 但是,您可以初始化单个成员 - 这是一个常量结构。见下文。

我还考虑过使 ImportantConstant1..3 成为非常量字段或函数调用,但两者似乎都逊色。

我不认为 getter 函数会逊色。编译器很可能会内联它们。考虑一下:

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 有多小。

于 2010-07-15T14:23:33.380 回答
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。使用构造委托有助于简化成员的初始化,这些成员最好一起初始化或可能相互依赖。

但是,在使用构造委托时要格外小心,因为函数调用(或构造函数调用)的函数参数的初始化顺序是未定义的,最终可能会以不正确的顺序初始化事物,或者以一种可能导致如果某些事情失败或引发异常,则资源泄漏。

于 2016-03-11T15:34:59.633 回答
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()) {}
};
于 2010-07-15T14:31:15.957 回答
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);
}
于 2010-07-15T14:46:16.690 回答
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;

当然,在一个真实的程序中,我会鼓励你将常量实际包装在某种接口后面,以这种方式公开它们是非常糟糕的做法,因为它使更改它们(使用另一种类型)变得非常困难。

另请注意,您不需要输出参数的指针,普通引用需要。

于 2010-07-15T15:37:01.847 回答