2010-03-22 30 views
5

Xét đoạn mã sau:Có phải hành vi không xác định trong trường hợp gọi hàm riêng trong danh sách khởi tạo không?

struct Calc 
{ 
    Calc(const Arg1 & arg1, const Arg2 & arg2, /* */ const ArgN & argn) : 
     arg1(arg1), arg2(arg2), /* */ argn(argn), 
     coef1(get_coef1()), coef2(get_coef2()) 
    { 
    } 

    int Calc1(); 
    int Calc2(); 
    int Calc3(); 

private: 
    const Arg1 & arg1; 
    const Arg2 & arg2; 
    // ... 
    const ArgN & argn; 

    const int coef1; // I want to use const because 
    const int coef2; //  no modification is needed. 

    int get_coef1() const { 
    // calc coef1 using arg1, arg2, ..., argn; 
    // undefined behavior?  
    } 
    int get_coef2() const { 
    // calc coef2 using arg1, arg2, ..., argn and coef1; 
    // undefined behavior? 
    } 

}; 

struct Calc không hoàn toàn được xác định khi tôi gọi get_coef1get_coef2 là mã này có hiệu lực? Tôi có thể nhận UB không?

+0

Nó hoạt động ... nhưng bạn tự đặt mình lên vì sự cố. Điều gì sẽ xảy ra khi người bảo trì đến, thêm 'argZ' sau các coefs, và sau đó sử dụng' argZ' trong tính toán? Bạn sẽ có UB của bạn ... –

Trả lời

8

12.6.2.8: Chức năng thành viên (bao gồm chức năng thành viên ảo, 10.3) có thể được gọi cho đối tượng đang được xây dựng. Tương tự, đối tượng đang được xây dựng có thể là toán hạng của toán tử typeid (5.2.8) hoặc của dynamic_cast (5.2.7). Tuy nhiên, nếu các hoạt động này được thực hiện trong bộ khởi tạo ctor (hoặc trong một hàm được gọi trực tiếp hoặc gián tiếp từ bộ khởi tạo ctor) trước khi tất cả các bộ khởi tạo mem cho các lớp cơ sở đã hoàn thành, kết quả của hoạt động không được xác định.

Vì vậy, bạn có thể khởi tạo thành viên lớp học theo cách này, nhưng không thể khởi tạo lớp cơ sở. Và, như những người khác đã chỉ ra, bạn nên biết thứ tự khởi tạo thành viên, nếu hàm của bạn sử dụng một số giá trị của chúng.

+0

+1 bởi vì nó là nhiều hơn đến điểm của những gì OP thực sự muốn biết. –

3

Vì các biến tính toán của bạn phụ thuộc vào đã được khởi tạo tại thời điểm cuộc gọi, nên không phải là hành vi không xác định. Xem câu hỏi this để biết thông tin liên quan.

+3

Và hãy nhớ rằng các thành viên được khởi tạo theo thứ tự trong đó các khai báo của chúng xuất hiện, không phải thứ tự mà chúng xuất hiện trong danh sách khởi tạo. Ở đây nó giống nhau nhưng lưu ý rằng chỉ cần đặt 'const int coef1;' ở trên 'const Arg1 & arg1;' (và giữ cho danh sách khởi tạo của bạn giống nhau) * sẽ * tạo UB. –

+0

Câu hỏi của tôi không phải về thứ tự khởi tạo. Thứ tự mà các khai báo của họ xuất hiện là ok. Tôi hỏi về hàm 'get_calc1'. Chẳng hạn, con trỏ này có hợp lệ không? 'arg2' được khởi tạo, đúng, đúng. Nhưng tôi có thể truy cập 'arg2' trong' get_calc1' không? –

+1

Thật tuyệt khi gọi các hàm thành viên chỉ tham chiếu đến các thuộc tính thành viên được khởi tạo. –

0

Không phải là không xác định, nhưng bạn phải hoàn toàn chắc chắn rằng các chức năng thành viên đó chỉ sử dụng giá trị được khởi tạo. Cũng lưu ý rằng các giá trị được khởi tạo theo thứ tự mà chúng xuất hiện trong lớpkhông phải là thứ tự chúng xuất hiện trong danh sách khởi tạo. Ví dụ:

struct Foo 
{ 
    int a, b; 
    int c; 
    Foo(): c(1), a(1), b(1) {} 
}; 

Trong constructor đó, các biến được khởi tạo theo thứ tự a, b, c sau đó, thứ tự trong danh sách có ý nghĩa gì. Vì vậy, nếu bạn muốn giá trị của a được khởi tạo bằng cách sử dụng một số phép tính trên bc thì bạn sẽ cần di chuyển khai báo a đến một điểm sau đó là bc.

+0

Thnks. Nhưng câu hỏi của tôi không phải là về thứ tự khởi tạo. Bạn có thể nhận thấy rằng trật tự đã được tất cả các quyền. –

+0

Tôi trả lời rằng trong vài từ đầu tiên. Nó không phải là không xác định, cung cấp thứ tự là đúng, mà nó là trong trường hợp của bạn, vì vậy bạn đang sử dụng tốt. –

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