Рассмотрите класс как этот:
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), который вычисляет три константы во времени выполнения. Предположим, что вычисление довольно сложно, и по сути производит все три значения сразу. Кроме того, результаты зависят от конфигурации сборки, таким образом, жесткое кодирование результаты не является опцией.
Существует ли разумный способ сохранить эти вычисленные значения в соответствующей константе двойные поля класса?
В противном случае можно ли предложить более естественный способ объявить такой класс в C++?
В C# я использовал бы статический класс со статическим конструктором здесь, но это не опция в C++. Я также считал создание ImportantConstant1.. 3 или поля неконстанты или вызовы функции, но оба кажутся нижними.
Единственный способ инициализировать поля константы, которые я нашел, состоит в том, чтобы использовать списки инициализатора, но не кажется возможным передать результаты мультивыходного вычисления в таком списке.
Почему вы не можете сделать:
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;
};
Вот так и превратить функцию вычислений в фабричную?
во-первых - вы можете творить зло: отбросьте конст в ComputeImportantConstants() и поместите туда значения. Не делайте этого, потому что тогда вы лжете компилятору, и он попытается найти самый неприятный способ окупиться.
second:
сделайте что-то вроде этого:
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; }
};
вы все равно можете улучшить этот класс, сделав его своего рода синглтоном или около того (так как вы хотите, чтобы вычисление выполнялось только один раз).
Вы можете переместить 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);
};
Единственный способ инициализировать константные поля, который я нашел, - это использовать списки инициализаторов, но не представляется возможным передать результаты вычислений с несколькими выходами в таком списке.
Это правда; однако вы можете инициализировать один член - структуру констант. Увидеть ниже.
Я также подумал о том, чтобы сделать 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
, а также все его поля являются постоянными, значения не могут быть изменены другими методами-членами - только в коде, который вы набросали в своем вопросе. Инициализация может быть
используется здесь, поскольку мы инициализируем одно значение: структуру.
Доступ к константам (скорее всего) будет таким же эффективным, как и раньше: предложение встроить функции, и компилятор, скорее всего, сделает это, учитывая, насколько малы геттеры.
Просто разделите объект на простую для инициализации и сложную часть, а затем инициализируйте сложную часть через конструктор копирования:
// 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()) {}
};
Ни один из приведенных выше ответов не обращал внимания на детали: здесь упоминается 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;
Конечно, в реальной программе я бы посоветовал вам на самом деле заключить константы в какой-то интерфейс, это действительно плохая практика - раскрывать их таким образом, потому что это очень затрудняет их изменение (с использованием другого типа).
Также обратите внимание, что вам не нужны указатели для выходных параметров, в отличие от простых ссылок.
Как насчет чего-то вроде этого:
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);
}