2010-03-09 109 views
59

Tôi đang bị chuột rút não ... làm cách nào để khởi tạo một mảng đối tượng đúng trong C++?C++: khởi tạo hàm tạo cho mảng

không cho mảng Ví dụ:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Bar { 
    Foo foo; 

    Bar() : foo(4) {} 
}; 

mảng Ví dụ:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    // ??? I know the following syntax is wrong, but what's correct? 
    Baz() : foo[0](4), foo[1](5), foo[2](6) {} 
}; 

chỉnh sửa: hoang dã & ý tưởng workaround điên được đánh giá cao, nhưng họ sẽ không giúp tôi trong trường hợp của tôi. Tôi đang làm việc trên một bộ xử lý nhúng trong đó std :: vector và các cấu trúc STL khác không có sẵn, và cách giải quyết rõ ràng là tạo một hàm tạo mặc định và có phương thức rõ ràng init() có thể được gọi sau thời gian xây dựng, để tôi không 't phải sử dụng initializers ở tất cả. (Đây là một trong những trường hợp mà tôi đã bị hư hỏng bởi từ khóa Java + final + linh hoạt với các nhà thầu.)

+0

lớp của bạn không constructible vì tất cả mọi thứ là 'private'. –

+5

(truy cập từ khóa còn lại để đơn giản sư phạm) –

+7

Sẽ không dễ dàng hơn khi sử dụng 'struct' thay cho' lớp' để đơn giản sư phạm? Tôi tìm thấy mã mà biên dịch dễ dàng hơn để tìm hiểu từ ;-) –

Trả lời

48

Không có cách nào. Bạn cần một hàm tạo mặc định cho các thành viên mảng và nó sẽ được gọi, sau đó, bạn có thể thực hiện bất kỳ khởi tạo nào bạn muốn trong hàm tạo.

+8

Thật không may, bạn nói đúng. 1 Lưu ý rằng cú pháp khởi tạo hợp nhất C++ 1x 'sẽ cho phép bạn thực hiện điều này. – sbi

+10

@sbi: Tôi vẫn gọi nó là "C++ 0x". Tất nhiên, đôi khi tôi cũng cho tôi 38 tuổi và hy vọng sẽ nghỉ hưu ở tuổi 3E. –

+2

@David: Tôi gọi nó là C++ 1x (hy vọng nó sẽ được phát hành trong vòng 9,5 năm tới). – sbi

1

Chỉ hàm khởi tạo mặc định mới có thể được gọi khi tạo đối tượng trong một mảng.

16

Ngay bây giờ, bạn không thể sử dụng danh sách trình khởi tạo cho thành viên mảng. Bạn đang mắc kẹt làm điều đó một cách khó khăn.

class Baz { 
    Foo foo[3]; 

    Baz() { 
     foo[0] = Foo(4); 
     foo[1] = Foo(5); 
     foo[2] = Foo(6); 
    } 
}; 

Trong C++ 0x bạn có thể viết:

class Baz { 
    Foo foo[3]; 

    Baz() : foo({4, 5, 6}) {} 
}; 
+0

Một hàm tạo một đối số sẽ được gọi cho một int trừ khi bạn khai báo hàm tạo rõ ràng. – jmanning2k

+0

thú vị ... Tôi có lẽ nên đã sử dụng một cái gì đó bên cạnh 'int' sau đó trong ví dụ của tôi, vì nó quá" dễ dàng "để đối phó với. :-) –

1

Trong trường hợp cụ thể khi mảng là thành viên dữ liệu của lớp bạn không có thể khởi tạo nó trong phiên bản hiện tại của ngôn ngữ. Không có cú pháp cho điều đó. Hoặc là cung cấp một hàm tạo mặc định cho các phần tử mảng hoặc sử dụng std::vector.

Một mảng độc lập có thể được khởi tạo với initializer tổng

Foo foo[3] = { 4, 5, 6 }; 

nhưng tiếc là không có cú pháp tương ứng để xem danh sách constructor khởi tạo.

0

Không có cú pháp xây dựng mảng nào được sử dụng trong ngữ cảnh này, ít nhất là không trực tiếp. Bạn có thể thực hiện những gì bạn đang cố gắng thực hiện một cái gì đó dọc theo dòng:

Bar::Bar() 
{ 
    static const int inits [] = {4,5,6}; 
    static const size_t numInits = sizeof(inits)/sizeof(inits[0]); 
    std::copy(&inits[0],&inits[numInits],foo); // be careful that there are enough slots in foo 
} 

... nhưng bạn sẽ cần phải cung cấp cho Foo một constructor mặc định.

2

Điều này dường như làm việc, nhưng tôi không tin đó là đúng:

#include <iostream> 

struct Foo { int x; Foo(int x): x(x) { } }; 

struct Baz { 
    Foo foo[3]; 

    static int bar[3]; 
    // Hmm... 
    Baz() : foo(bar) {} 
}; 

int Baz::bar[3] = {4, 5, 6}; 

int main() { 
    Baz z; 
    std::cout << z.foo[1].x << "\n"; 
} 

Output:

$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit 
g++ -pedantic arrayinit.cpp -o arrayinit 
5 

Caveat emptor.

Chỉnh sửa: không, Comeau từ chối.

Chỉnh sửa khác: Đây là loại gian lận, nó chỉ đẩy khởi tạo mảng từng thành viên đến một vị trí khác.Vì vậy, nó vẫn đòi hỏi Foo để có một constructor mặc định, nhưng nếu bạn không có std::vector sau đó bạn có thể thực hiện cho chính mình tối thiểu tuyệt đối bạn cần:

#include <iostream> 

struct Foo { 
    int x; 
    Foo(int x): x(x) { }; 
    Foo(){} 
}; 

// very stripped-down replacement for vector 
struct Three { 
    Foo data[3]; 
    Three(int d0, int d1, int d2) { 
     data[0] = d0; 
     data[1] = d1; 
     data[2] = d2; 
    } 
    Foo &operator[](int idx) { return data[idx]; } 
    const Foo &operator[](int idx) const { return data[idx]; } 
}; 

struct Baz { 
    Three foo; 

    static Three bar; 
    // construct foo using the copy ctor of Three with bar as parameter. 
    Baz() : foo(bar) {} 
    // or get rid of "bar" entirely and do this 
    Baz(bool) : foo(4,5,6) {} 
}; 

Three Baz::bar(4,5,6); 

int main() { 
    Baz z; 
    std::cout << z.foo[1].x << "\n"; 
} 

z.foo không phải là thực sự là một mảng, nhưng có vẻ về nhiều như một vector. Việc thêm các hàm begin()end() vào Ba là tầm thường.

+0

+1 để cố gắng thực hiện một cách giải quyết –

+0

... và điều này mang lại cho tôi một số ý tưởng có thể hoạt động tốt cho tình hình của tôi rõ ràng hơn những gì tôi có. cảm ơn! –

0

Ý tưởng từ một tâm trí xoắn:

class mytwistedclass{ 
static std::vector<int> initVector; 
mytwistedclass() 
{ 
    //initialise with initVector[0] and then delete it :-) 
} 

}; 

bây giờ thiết lập initVector này để một cái gì đó u muốn trước khi u tạo một đối tượng. Sau đó, các đối tượng của bạn được khởi tạo với các tham số của bạn.

7

Thật không may là không có cách nào để khởi tạo thành viên mảng cho đến C++ 0x.

Bạn có thể sử dụng lệnh std :: vector và push_back các cá thể Foo trong phần thân hàm tạo.

Bạn có thể cung cấp cho Foo một hàm tạo mặc định (có thể là riêng tư và biến Baz thành một người bạn).

Bạn có thể sử dụng một đối tượng mảng copyable (tăng hoặc std :: tr1) và khởi tạo từ một mảng tĩnh:

#include <boost/array.hpp> 

struct Baz { 

    boost::array<Foo, 3> foo; 
    static boost::array<Foo, 3> initFoo; 
    Baz() : foo(initFoo) 
    { 

    } 
}; 

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 }; 
+0

+1. Tự hỏi tại sao không ai đến với điều này, cho đến khi tôi nhìn thấy câu trả lời của bạn. 'mảng' là tầm thường để thực hiện, và nó không phải là hoang dã cũng không điên. Bạn có thể viết một hàm như 'mảng create() {mảng a = {...}; trả lại a; } 'để tránh biến tĩnh, quá. –

+0

Có vẻ hiển nhiên với tôi, ngay cả khi sự hỗ trợ cho các mẫu yếu trên trình biên dịch đích (không có 'std :: vector' có vẻ giống cá) một cách tiếp cận thế hệ sẽ hoạt động (preprocessor hoặc script tạo các lớp cần thiết). –

3

Bạn có thể sử dụng C++ 0xauto từ khóa cùng với chuyên môn mẫu ví dụ: một hàm có tên boost::make_array() (tương tự make_pair()). Đối với trường hợp nơi N là 1 hoặc 2 đối số thì chúng ta có thể viết biến Một như

namespace boost 
{ 
/*! Construct Array from @p a. */ 
template <typename T> 
boost::array<T,1> make_array(const T & a) 
{ 
    return boost::array<T,2> ({{ a }}); 
} 
/*! Construct Array from @p a, @p b. */ 
template <typename T> 
boost::array<T,2> make_array(const T & a, const T & b) 
{ 
    return boost::array<T,2> ({{ a, b }}); 
} 
} 

biến thể B như

namespace boost { 
/*! Construct Array from @p a. */ 
template <typename T> 
boost::array<T,1> make_array(const T & a) 
{ 
    boost::array<T,1> x; 
    x[0] = a; 
    return x; 
} 
/*! Construct Array from @p a, @p b. */ 
template <typename T> 
boost::array<T,2> make_array(const T & a, const T & b) 
{ 
    boost::array<T,2> x; 
    x[0] = a; 
    x[1] = b; 
    return x; 
} 
} 

GCC-4.6 với -std=gnu++0x-O3 tạo ra mã nhị phân chính xác giống nhau cho

auto x = boost::make_array(1,2); 

sử dụng cả hai MộtB vì nó làm cho

boost::array<int, 2> x = {{1,2}}; 

Đối người dùng định nghĩa loại (UDT), tuy nhiên, kết quả biến thể B trong một bản sao constructor thêm, mà thường làm chậm mọi thứ, và do đó nên tránh.

Lưu ý rằng boost::make_array lỗi khi gọi nó với chữ rõ ràng char mảng như trong trường hợp sau đây

auto x = boost::make_array("a","b"); 

Tôi tin rằng đây là một điều tốt vì const char* literals có thể lừa đảo trong việc sử dụng chúng.

variadic mẫu, có sẵn trong GCC kể từ 4.5, hơn nữa có thể được sử dụng làm giảm tất cả các mẫu chuyên môn đang nồi hơi-tấm cho mỗi N thành một đơn mẫu định nghĩa của boost::make_array() định nghĩa là

/*! Construct Array from @p a, @p b. */ 
template <typename T, typename ... R> 
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b) 
{ 
    return boost::array<T,1+sizeof...(R)>({{ a, b... }}); 
} 

Điều này hoạt động khá nhiều như chúng ta mong đợi. Đối số đầu tiên xác định boost::array đối số mẫu T và tất cả đối số khác được chuyển đổi thành T. Đối với một số trường hợp, điều này có thể không mong muốn, nhưng tôi không chắc làm thế nào nếu điều này có thể chỉ định bằng cách sử dụng các mẫu variadic.

Có lẽ boost::make_array() nên đi vào Thư viện nâng cao?

+0

cảm ơn nhưng C++ 0x không có sẵn trên bộ vi xử lý nhúng thấp (trình biên dịch C++ đủ khó tìm) –

-3
class C 
{ 
    static const int myARRAY[10]; // only declaration !!! 

    public: 
    C(){} 
    } 

const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9}; // here is definition 

int main(void) 
{ 
    C myObj; 
    } 
25

Chỉ cần cập nhật câu hỏi này cho C++ 11, đây là bây giờ cả hai có thể làm và rất tự nhiên:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    Baz() : foo{{4}, {5}, {6}} { } 
}; 

Những niềng răng cũng có thể được elided cho một thậm chí ngắn gọn hơn:

struct Baz { 
    Foo foo[3]; 

    Baz() : foo{4, 5, 6} { } 
}; 

Mà có thể dễ dàng được mở rộng đến các mảng đa chiều quá:

struct Baz { 
    Foo foo[3][2]; 

    Baz() : foo{1, 2, 3, 4, 5, 6} { } 
}; 
+0

Có cách nào tốt để khởi tạo Foo foo [3] [2] ;? – dshin

+2

@dshin Cùng một cách. Hầu hết các dấu ngoặc đơn: ': foo {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}' hoặc nhỏ hơn 'foo {{1, 2} , {3, 4}, {5, 6}} 'hoặc ít nhất là dấu ngoặc nhọn' foo {1, 2, 3, 4, 5, 6} '. – Barry

+3

Có giải pháp nào khi hàm tạo 'Foo' được khai báo rõ ràng không? – dshin

-1

trong visual studio 2012 hoặc cao hơn, bạn có thể làm như thế này

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    Baz() : foo() { } 
}; 
0

Bạn có thể làm điều đó, nhưng nó không đẹp:

#include <iostream> 

class A { 
    int mvalue; 
public: 
    A(int value) : mvalue(value) {} 
    int value() { return mvalue; } 
}; 

class B { 
    // TODO: hack that respects alignment of A.. maybe C++14's alignof? 
    char _hack[sizeof(A[3])]; 
    A* marr; 
public: 
    B() : marr(reinterpret_cast<A*>(_hack)) { 
     new (&marr[0]) A(5); 
     new (&marr[1]) A(6); 
     new (&marr[2]) A(7); 
    } 

    A* arr() { return marr; } 
}; 

int main(int argc, char** argv) { 
    B b; 
    A* arr = b.arr(); 
    std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n"; 
    return 0; 
} 

Nếu bạn đặt này trong mã của bạn, tôi hy vọng bạn có lý do rất tốt.

0

Đây là giải pháp của tôi để bạn tham khảo:

struct Foo 
{ 
    Foo(){}//used to make compiler happy! 
    Foo(int x){/*...*/} 
}; 

struct Bar 
{ 
    Foo foo[3]; 

    Bar() 
    { 
     //initialize foo array here: 
     for(int i=0;i<3;++i) 
     { 
      foo[i]=Foo(4+i); 
     } 
    } 
}; 
Các vấn đề liên quan