2008-09-23 30 views
14

Trong C++, không thể khởi tạo thành viên mảng trong danh sách khởi tạo, do đó các đối tượng thành viên phải có các hàm tạo mặc định và chúng nên được khởi tạo đúng cách trong hàm tạo. Có cách nào khác (hợp lý) cho việc này ngoài việc không sử dụng mảng không?Bất kỳ giải pháp nào cho việc khởi tạo mảng thành viên không tĩnh?

[Bất kỳ thứ gì có thể được khởi tạo chỉ sử dụng danh sách khởi tạo trong ứng dụng của chúng tôi là thích hợp hơn khi sử dụng hàm tạo, vì dữ liệu đó có thể được phân bổ và khởi tạo bởi trình biên dịch và trình liên kết và mỗi chu kỳ xung nhịp CPU, ngay cả trước main . Tuy nhiên, không phải lúc nào cũng có thể có một hàm tạo mặc định cho mọi lớp, và bên cạnh đó, khởi tạo lại dữ liệu một lần nữa trong hàm tạo thay vì đánh bại mục đích.]

Ví dụ: Tôi muốn có một cái gì đó như thế này (nhưng điều này không làm việc):

class OtherClass { 
private: 
    int data; 
public: 
    OtherClass(int i) : data(i) {}; // No default constructor! 
}; 

class Foo { 
private: 
    OtherClass inst[3]; // Array size fixed and known ahead of time. 
public: 
    Foo(...) 
     : inst[0](0), inst[1](1), inst[2](2) 
     {}; 
}; 

Cách giải quyết duy nhất tôi biết là không cho mảng một:

class Foo { 
private: 
    OtherClass inst0; 
    OtherClass inst1; 
    OtherClass inst2; 
    OtherClass *inst[3]; 
public: 
    Foo(...) 
     : inst0(0), inst1(1), inst2(2) { 
     inst[0]=&inst0; 
     inst[1]=&inst1; 
     inst[2]=&inst2; 
    }; 
}; 

Sửa: Cần nhấn mạnh rằng OtherClass không có hàm tạo mặc định và rất mong muốn người liên kết có thể phân bổ bất kỳ bộ nhớ nào cần thiết (một hoặc nhiều phiên bản tĩnh của Foo sẽ được tạo), sử dụng heap về cơ bản là verboten. Tôi đã cập nhật các ví dụ ở trên để làm nổi bật điểm đầu tiên.

Trả lời

4

Một cách giải quyết khác là tránh trình biên dịch gọi hàm dựng khác của OtherClass và gọi nó bằng cách sử dụng vị trí mới để khởi tạo nó theo bất kỳ cách nào bạn cần. Ví dụ:

class Foo 
    { 
    private: 
    char inst[3*sizeof(OtherClass)]; // Array size fixed. OtherClass has no default ctor. 

    // use Inst to access, not inst 
    OtherClass &Inst(int i) {return (OtherClass *)inst+i;} 
    const OtherClass &Inst(int i) const {return (const OtherClass *)inst+i;} 
    public: 
    Foo(...) 
    { 
     new (Inst(0)) OtherClass(...); 
     new (Inst(1)) OtherClass(...); 
     new (Inst(2)) OtherClass(...); 
    } 
    ~Foo() 
    { 
     Inst(0)->~OtherClass(); 
     Inst(1)->~OtherClass(); 
     Inst(2)->~OtherClass(); 
    } 
    }; 

Để phục vụ cho các yêu cầu liên kết có thể có của các OtherClass, bạn có thể cần phải sử dụng __declspec (sắp xếp (x)) nếu làm việc trong VisualC++, hoặc sử dụng một loại khác với char như:

Type inst[3*(sizeof(OtherClass)+sizeof(Type)-1)/sizeof(Type)]; 

... trong đó Loại là int, gấp đôi, dài hoặc mô tả các yêu cầu căn chỉnh.

+0

Đừng quên thêm "#include " cho vị trí mới :) – Drealmer

+0

Tôi sẽ phải kiểm tra hiệu suất của vị trí mới trên nền tảng của mình, nhưng nếu không, điều này có vẻ là giải pháp làm việc, mặc dù có thể không dễ hiểu đồng nghiệp của tôi. :-) –

+0

Tôi đã chọn câu trả lời này, mặc dù tôi có thể sẽ giữ phiên bản của riêng mình trong thực tế, vì tôi nghĩ nó dễ đọc hơn và nhanh hơn, mặc dù nó sử dụng thêm bộ nhớ cho mảng con trỏ tiện lợi. –

2

Thành viên dữ liệu nào trong OtherClass? Liệu giá trị khởi tạo có đủ cho lớp đó không?

Nếu giá trị khởi tạo là đủ, sau đó bạn có thể đánh giá cao-khởi tạo một mảng trong danh sách thành viên khởi tạo:

class A { 
public: 
    A() 
    : m_a() // All elements are value-initialized (which for int means zero'd) 
    { 
    } 

private: 
    int m_a[3]; 
}; 

Nếu loại phần tử mảng của bạn là loại lớp, sau đó các nhà xây dựng mặc định sẽ được gọi.

CHỈNH SỬA: Chỉ để làm rõ nhận xét từ Drealmer.

Trong trường hợp loại phần tử không phải là POD, thì nó phải có "hàm tạo mặc định có thể truy cập" (như đã nêu ở trên). Nếu trình biên dịch không thể gọi hàm tạo mặc định, thì giải pháp này sẽ không hoạt động.

Ví dụ sau đây, sẽ không làm việc với phương pháp này:

class Elem { 
public: 
    Elem (int); // User declared ctor stops generation of implicit default ctor 
}; 

class A { 
public: 
    A() 
    : m_a()   // Compile error: No default constructor 
    {} 

private: 
    Elem m_a[10]; 
}; 
+0

Nó sẽ không làm việc với một mảng của một loại không có constructor mặc định, nhưng lừa tốt đẹp anyway. – Drealmer

+0

Việc khởi tạo giá trị của mảng thành viên không được hỗ trợ theo tiêu chuẩn. Nó không hoạt động với Visual Studio mặc dù. – andriej

+0

@ user467799: C++ '03 8.5/5: 'Để giá trị khởi tạo một đối tượng kiểu T có nghĩa là:' ... '- nếu T là một kiểu mảng, thì mỗi phần tử được khởi tạo giá trị'. –

0

thành viên mảng không được khởi tạo theo mặc định.Vì vậy, bạn có thể sử dụng một hàm trợ giúp tĩnh để khởi tạo và lưu trữ kết quả của hàm trợ giúp trong một thành viên.

#include "stdafx.h" 
#include <algorithm> 
#include <cassert> 

class C { 
public: // for the sake of demonstration... 
    typedef int t_is[4] ; 
    t_is is; 
    bool initialized; 

    C() : initialized(false) 
    { 
    } 

    C(int deflt) 
    : initialized(sf_bInit(is, deflt)) 
    {} 

    static bool sf_bInit(t_is& av_is, const int i){ 
    std::fill(av_is, av_is + sizeof(av_is)/sizeof(av_is[0]), i); 
    return true; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 

    C c(1), d; 

    assert(c.is[0] == 1); 

    return 0; 
} 

Đáng chú ý là trong tiêu chuẩn tiếp theo, chúng sẽ hỗ trợ khởi tạo mảng.

+0

Vẫn còn gốc của vấn đề vẫn còn, bạn cần một constructor mặc định cho loại phần tử mảng. – Drealmer

0

Sử dụng thừa kế để tạo đối tượng proxy

class ProxyOtherClass : public OtherClass { 
public: 
    ProxyOtherClass() : OtherClass(0) {} 
}; 

class Foo { 
private: 
    ProxyOtherClass inst[3]; // Array size fixed and known ahead of time. 
public: 
    Foo(...) {} 
}; 
+0

Tôi có thể đã đọc câu trả lời quá nhanh, nhưng tôi không nghĩ rằng nó sẽ làm việc vì mỗi cá thể OtherClass cần các tham số khác nhau cho hàm tạo. –

0

Và những gì về việc sử dụng mảng các con trỏ thay vì mảng các đối tượng? Ví dụ:

class Foo { 
private: 
    OtherClass *inst[3]; 
public: 
    Foo(...) { 
     inst[0]=new OtherClass(1); 
     inst[1]=new OtherClass(2); 
     inst[2]=new OtherClass(3); 
    }; 

    ~Foo() { 
     delete [] inst; 
    } 

}; 
+0

Toán tử mới (khi được sử dụng như trên) sẽ phân bổ các đối tượng mới trên heap, điều không được phép. –

0

Bạn nói "Bất cứ điều gì có thể được khởi tạo chỉ sử dụng danh sách khởi là trong ứng dụng của chúng tôi xa thích hợp hơn để sử dụng các nhà xây dựng, như dữ liệu có thể được phân bổ và khởi tạo bởi trình biên dịch và mối liên kết, và mỗi chu kỳ xung nhịp CPU ".

Vì vậy, không sử dụng các nhà thầu. Đó là, không sử dụng "trường hợp" thông thường. Khai báo mọi thứ tĩnh. Khi bạn cần một "cá thể" mới, tạo một khai báo tĩnh mới, có khả năng nằm ngoài bất kỳ lớp nào. Sử dụng cấu trúc với các thành viên công cộng nếu bạn có. Sử dụng C nếu bạn phải.

Bạn đã trả lời câu hỏi của riêng mình. Constructors và destructors chỉ hữu dụng trong các môi trường có nhiều phân bổ và deallocation. Điều gì tốt là hủy diệt nếu mục tiêu là cho càng nhiều dữ liệu càng tốt để được phân bổ tĩnh, và vì vậy những gì tốt là xây dựng mà không bị phá hủy? Địa ngục với cả hai người họ.

1

Một phương pháp tôi thường sử dụng để làm cho thành viên của lớp "xuất hiện" ở trên ngăn xếp (mặc dù thực sự được lưu trữ trên heap):

class Foo { 
private: 
    int const (&array)[3]; 
    int const (&InitArray() const)[3] { 
     int (*const rval)[3] = new int[1][3]; 
     (*rval)[0] = 2; 
     (*rval)[1] = 3; 
     (*rval)[2] = 5; 
     return *rval; 
    } 
public: 
    explicit Foo() : array(InitArray()) { } 
    virtual ~Foo() { delete[] &array[0]; } 
};
Đối với khách hàng thuộc lớp của bạn, mảng có vẻ thuộc loại "int const [3 ] ". Kết hợp mã này với vị trí mới và bạn cũng có thể thực sự khởi tạo các giá trị theo quyết định của mình bằng cách sử dụng bất kỳ hàm tạo nào bạn muốn. Hi vọng điêu nay co ich.

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