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

実行時に 3 つの定数を計算するルーチン (ComputeImportantConstants) があります。計算がかなり複雑で、本質的に 3 つの値すべてを一度に生成するとします。さらに、結果はビルド構成に依存するため、結果をハードコーディングすることはできません。

これらの計算された値をクラスの対応する const double フィールドに格納する賢明な方法はありますか?

そうでない場合、C++ でそのようなクラスを宣言するより自然な方法を提案できますか?

C# では、ここで静的コンストラクターを持つ静的クラスを使用しますが、C++ ではそれはオプションではありません。また、ImportantConstant1..3 を非 const フィールドまたは関数呼び出しにすることも検討しましたが、どちらも劣っているようです。

私が見つけた 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 をキャストし、そこに値を配置します。ただし、そうしないでください。コンパイラに嘘をつき、最も厄介な返済方法を見つけようとするからです。

2番目:

次のようにします。

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

このクラスをある種のシングルトンなどにすることで、このクラスを改善することができます (計算を 1 回だけ実行する必要があるため)。

于 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 を非 const フィールドまたは関数呼び出しにすることも検討しましたが、どちらも劣っているようです。

ゲッター関数が劣っているとは思いません。コンパイラは、おそらくそれらをインライン化します。このことを考慮:

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、他のメンバーメソッドによって値を変更することはできません-質問でスケッチしたコードだけです。単一の値である構造体を初期化するため、ここで初期化を使用できます。

定数へのアクセスは以前と同じくらい効率的です (ほとんどの場合): 関数をインライン化することを提案し、ゲッターがどれほど小さいかを考えると、コンパイラーはそうする可能性が非常に高くなります。

于 2010-07-15T14:23:33.380 に答える
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

初期化が簡単な部分と複雑な部分に分割し、コピー コンストラクターを介して複雑な部分を初期化します。

// 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

上記の答えはどれも詳細に注意を払っていないようでした: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 に答える