2010-07-15 27 views
11

Hãy xem xét một lớp học như thế này:khởi Complex ruộng const

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

Có một thói quen (ComputeImportantConstants) mà tính ba hằng số khi chạy. Giả sử tính toán khá phức tạp và vốn đã tạo ra cả ba giá trị cùng một lúc. Hơn nữa, các kết quả phụ thuộc vào cấu hình xây dựng, do đó, mã hóa cứng các kết quả không phải là một tùy chọn.

Có cách nào hợp lý để lưu trữ các giá trị được tính toán này trong các trường đôi const tương ứng của lớp không?

Nếu không, bạn có thể đề xuất cách tự nhiên hơn để khai báo một lớp như vậy trong C++ không?

Trong C# tôi sẽ sử dụng một lớp tĩnh với một hàm dựng tĩnh ở đây, nhưng đó không phải là một tùy chọn trong C++. Tôi cũng đã xem xét việc đưa ra các trường quan trọng không phải là const hoặc các cuộc gọi hàm, nhưng cả hai đều có vẻ kém hơn.

Cách duy nhất để khởi tạo các trường const mà tôi tìm thấy là use initializer lists, nhưng dường như không thể chuyển kết quả của tính toán nhiều đầu ra trong danh sách như vậy.

+0

Nếu có thể, bạn có thể cho biết cách 'ComputeImportantConstants' được triển khai không? Nó có dài không? Làm thế nào để ba hằng số tương tác, những yếu tố nào khác có liên quan? –

Trả lời

9

Tại sao bạn không thể làm:

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

Giống như điều đó và có chức năng tính toán lần lượt vào một chức năng nhà máy?

+0

một ý tưởng cải tiến: tạo một biến tĩnh trong ComputeImportantConstants() và trả về biến này sau khi mọi thứ được tính toán. Bằng cách này, các cuộc gọi tiếp theo của ComputeImportantConstants không kích hoạt tính toán bổ sung. –

+1

C++ nitpick: trả về 'const double' từ một hàm không có ý nghĩa nhiều. Nó chỉ làm cho cuộc sống khó khăn hơn cần thiết cho người gọi mà không cần cải thiện an ninh. Các biến thành viên được trả về bởi giá trị, sau khi tất cả. –

+0

Tốt nitpick và bạn nói đúng. – wheaties

5

trước tiên - bạn có thể làm điều ác: bỏ đi const trong ComputeImportantConstants() và đặt các giá trị ở đó. Đừng làm điều đó mặc dù, bởi vì sau đó bạn nói dối với trình biên dịch và nó sẽ cố gắng tìm cách nastiest để trả lại.

thứ hai:

làm điều gì đó như thế này:

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

bạn vẫn có thể cải thiện lớp này bằng cách làm cho nó một số loại singleton hoặc lâu hơn (vì bạn muốn tính toán được thực hiện một lần duy nhất).

3

Bạn có thể di chuyển const trường để một lớp cơ sở, và sau đó vượt qua một lớp wrapper để khởi tạo chúng:

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

Hoạt động tốt; chỉ cần thay đổi thành thừa kế MyBase công khai và làm cho các trường dữ liệu kép được công khai. Ngoài ra, trong mã thực sự ba giá trị đã được truyền xung quanh trong một cấu trúc vì vậy tôi không cần một cấu trúc Initializer thêm. (Tôi thực sự đã viết câu hỏi của tôi bằng cách sử dụng cấu trúc đó ...) –

2

Cách duy nhất để khởi tạo các lĩnh vực const mà tôi tìm thấy là sử dụng danh sách khởi tạo, nhưng dường như không thể chuyển kết quả của tính toán đa đầu ra trong danh sách như vậy.

Đó là sự thật; tuy nhiên, bạn có thể khởi tạo một thành viên - đó là cấu trúc của các hằng số. Xem bên dưới.

Tôi cũng đã cân nhắc đưa ra các trường không quan trọng hoặc các cuộc gọi hàm, nhưng cả hai đều có vẻ kém hơn.

Tôi không nghĩ rằng hàm getter sẽ kém hơn. Trình biên dịch sẽ rất có khả năng nội tuyến chúng.Xem xét việc này:

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

Kể từ m_constants cũng như tất cả các lĩnh vực của nó là hằng số, các giá trị không thể thay đổi bằng các phương pháp thành viên khác - chỉ trong mã bạn phác thảo trong câu hỏi của bạn. Việc khởi tạo có thể là được sử dụng ở đây vì chúng tôi khởi tạo một giá trị đơn lẻ: một cấu trúc.

Truy cập vào các hằng số (rất có thể) để có hiệu quả như trước: đề xuất nội tuyến các hàm và trình biên dịch có khả năng làm như vậy với mức độ nhỏ của getters.

1

Chỉ cần chia ra các điều thành phần đó là đơn giản để khởi tạo và phần phức tạp, và khởi tạo các phần phức tạp thông qua constructor sao chép:

// 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()) {} 
}; 
+0

Sử dụng thừa kế ở đây có vẻ đau đớn đối với tôi. –

1

gì về một cái gì đó như thế:

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

Không có câu trả lời nào ở trên dường như chú ý đến một chi tiết: static được đề cập ở đây, vì vậy các hằng số này dường như độc lập với thể hiện thực tế của lớp.

Nói cách khác: đó là các hằng số toàn cầu. Như bạn đã đoán, sự hiện diện của từ khóa const là quan trọng ở đây, vì tối ưu hóa trình biên dịch sẽ áp dụng.

Dù sao, ý tưởng là sử dụng cấu trúc trợ giúp.

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

Tất nhiên, trong một chương trình thực tế, tôi sẽ khuyến khích bạn thực sự quấn hằng đằng sau một số loại giao diện, đó là thực hành thực sự xấu để lộ chúng theo cách này, vì nó làm thay đổi chúng (sử dụng loại khác) rất khó.

Cũng lưu ý rằng bạn không cần con trỏ cho tham số đầu ra, tham chiếu đơn giản làm.

2

Để sửa đổi câu trả lời được chấp nhận, xin lưu ý rằng, với C++ 11 bạn có thể thực hiện các thủ thuật rất gọn gàng. Ví dụ, vấn đề ban đầu của bạn có thể được giải quyết với một phái đoàn lambda và xây dựng như sau:

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

Hoặc tốt hơn, di chuyển mã khởi tạo từ computeImportantConstants vào lambda trong các nhà xây dựng, nếu có thể. Trong thực tế, việc sử dụng các cuộc gọi lambda để khởi tạo các thành viên không đổi là một thủ thuật rất tiện dụng, đặc biệt là vì bạn cũng có thể ràng buộc và/hoặc chuyển các đối số cho lambda. Và việc sử dụng đoàn xây dựng giúp dễ dàng khởi tạo các thành viên có thể được khởi tạo tốt nhất với nhau hoặc có thể phụ thuộc lẫn nhau. Tuy nhiên, hãy thận trọng khi sử dụng nhóm xây dựng, vì thứ tự khởi tạo của các đối số hàm cho một cuộc gọi hàm (hoặc gọi hàm dựng) là không xác định và có thể kết thúc khởi tạo mọi thứ theo thứ tự không chính xác hoặc theo cách thức có thể dẫn đến rò rỉ tài nguyên nếu có điều gì đó không thành công hoặc ném một ngoại lệ.

Các vấn đề liên quan