2009-04-17 37 views
12

Tôi có một đối tượng phức tạp (đối với tôi) với khoảng 20 thành viên dữ liệu, nhiều trong số đó là con trỏ đến các lớp khác. Vì vậy, đối với hàm tạo, tôi có một danh sách khởi tạo phức tạp và dài. Lớp này cũng có một tá các hàm tạo khác nhau, phản ánh các cách khác nhau mà lớp có thể được tạo ra. Hầu hết các mục được khởi tạo này không thay đổi giữa các hàm tạo khác nhau này. Quan tâm của tôi ở đây là bây giờ tôi có một mâm lớn được sao chép (hoặc phần lớn được sao chép) mã, nếu tôi cần thêm một thành viên mới vào lớp, có thể không đưa nó vào mỗi danh sách khởi tạo của hàm dựng.Xử lý một lớp với danh sách khởi tạo dài và nhiều hàm tạo?

class Object 
{ 
    Object(); 
    Object(const string &Name); 
    Object (const string &Name, const string &path); 
    Object (const string &Name, const bool loadMetadata); 
    Object (const string &Name, const string &path, const bool loadMetadata); 
} 

Object::Object() : 
    name(), 
    parent_index (0), 
    rowData (new MemoryRow()), 
    objectFile(), 
    rows (new MemoryColumn (object_constants::RowName, OBJECTID, object_constants::ROWS_OID)), 
    cols (new MemoryColumn (object_constants::ColName, OBJECTID, object_constants::COLS_OID)), 
    objectName (new MemoryColumn(object_constants::ObjName, STRING, object_constants::short_name_len, object_constants::OBJECTNAME_OID)), 
    parent  (new MemoryColumn(object_constants::ParentName, STRING, object_constants::long_name_len, object_constants::PARENT_OID)), 
    parentIndex (new MemoryColumn(object_constants::ParentIndex, OBJECTID, object_constants::PARENTINDEX_OID)), 
    childCount (new MemoryColumn (object_constants::ChildCount, INTEGER, object_constants::CHILD_COUNT_OID)), 
    childList (new MemoryColumn (object_constants::ChildList, STRING, object_constants::long_name_len, object_constants::CHILD_OID)), 
    columnNames (new MemoryColumn (object_constants::ColumnNames, STRING, object_constats::short_name_len, object_constants::COLUMN_NAME)), 
    columnTypes (new MemoryColumn (object_constants::ColumnTypes, INTEGER, object_constants::COLUMN_TYPE)), 
    columnSizes (new MemoryColumn (object_constants::ColumnSizes, INTEGER, object_constants::COLUMN_SIZE)) 
{} 

Sau đó lặp lại như trên cho các nhà thầu khác. Có cách nào thông minh của việc sử dụng constructor mặc định cho điều này, sau đó sửa đổi các kết quả cho các nhà xây dựng khác?

Trả lời

13

Làm thế nào về refactor các trường chung vào một lớp cơ sở. Hàm khởi tạo mặc định cho lớp cơ sở sẽ xử lý khởi tạo cho nhiều trường mặc định. Sẽ giống như thế này:

class BaseClass { 
    public: 
    BaseClass(); 
}; 

class Object : public BaseClass 
{ 
    Object(); 
    Object(const string &Name); 
    Object (const string &Name, const string &path); 
    Object (const string &Name, const bool loadMetadata); 
    Object (const string &Name, const string &path, const bool loadMetadata); 
}; 

BaseClass::BaseClass() : 
    parent_index (0), 
    rowData (new MemoryRow()), 
    objectFile(), 
    rows (new MemoryColumn (object_constants::RowName, OBJECTID, object_constants::ROWS_OID)), 
    cols (new MemoryColumn (object_constants::ColName, OBJECTID, object_constants::COLS_OID)), 
    objectName (new MemoryColumn(object_constants::ObjName, STRING, object_constants::short_name_len, object_constants::OBJECTNAME_OID)), 
    parent  (new MemoryColumn(object_constants::ParentName, STRING, object_constants::long_name_len, object_constants::PARENT_OID)), 
    parentIndex (new MemoryColumn(object_constants::ParentIndex, OBJECTID, object_constants::PARENTINDEX_OID)), 
    childCount (new MemoryColumn (object_constants::ChildCount, INTEGER, object_constants::CHILD_COUNT_OID)), 
    childList (new MemoryColumn (object_constants::ChildList, STRING, object_constants::long_name_len, object_constants::CHILD_OID)), 
    columnNames (new MemoryColumn (object_constants::ColumnNames, STRING, object_constats::short_name_len, object_constants::COLUMN_NAME)), 
    columnTypes (new MemoryColumn (object_constants::ColumnTypes, INTEGER, object_constants::COLUMN_TYPE)), 
    columnSizes (new MemoryColumn (object_constants::ColumnSizes, INTEGER, object_constants::COLUMN_SIZE)) 
{} 

constructors Object bạn nên xem xét một chút dễ quản lý hơn, bây giờ:

Object::Object() : BaseClass() {} 
Object::Object (const string &Name): BaseClass(), name(Name) {} 
Object::Object (const string &Name, const string &path): BaseClass(), name(Name), path_(path){} 
Object::Object (const string &Name, const bool loadMetadata): BaseClass(), name(Name){} 
Object::Object (const string &Name, const string &path, const bool loadMetadata): BaseClass(), path_(path) {} 

tương tự trong thiên nhiên để trả lời Iraimbilanja, nhưng tránh thêm một nội lớp để truy cập dữ liệu, điều này có thể ảnh hưởng rất nhiều đến mã hiện có. Tuy nhiên, nếu bạn đã có một hệ thống phân cấp lớp, có thể rất khó để đưa nó vào một lớp cơ sở.

+3

có thể muốn sử dụng quyền thừa kế riêng vì đây là chi tiết triển khai. –

+0

hoặc đặt baseclass trong một không gian tên trống? – jiggunjer

4

Boost::Parameter giúp dễ dàng triển khai Named Parameter Idiom. Hãy xem thread trên SO này. Điều này có thể không chính xác những gì bạn cần, nhưng cung cấp cho một số linh hoạt khi bạn muốn chuyển tiếp cuộc gọi đến ctor mặc định.

+0

bạn có thể giải thích cách thành ngữ thông số được đặt tên có liên quan không? –

+0

Liên kết thứ hai. – dirkgently

4

Không được làm với các nhà xây dựng, nhưng tại sao bạn nghĩ rằng bạn phải tạo tất cả các đối tượng con đó một cách động với mới? Đây không phải là một ý tưởng hay - bạn nên tránh tạo ra năng động bất cứ khi nào có thể. Đừng làm cho tất cả những con trỏ thành viên đó - biến chúng trở thành những đối tượng thực sự.

+1

Điều đó làm việc một số thời gian. Điều gì sẽ xảy ra nếu lớp học trở nên rất lớn và hầu như không được sử dụng? Điều gì nếu nó cần thiết để sao chép các đối tượng lớp học khá thường xuyên? Điều gì sẽ xảy ra nếu các thành viên khác nhau có thể chia sẻ các subobjects? –

2

Bạn có thể chia sẻ mã chung của họ trong một hàm thành viên init() riêng tư.

Ví dụ:

class Object 
{ 
public: 
    Object(const string &Name); 
    Object(const string &Name, const string &path); 
    ... 
private: 
    void init(); 
}; 

Object::Object(const string &Name) 
{ 
    init(); 
    ... 
} 

Object::Object(const string &Name, const string &path) 
{ 
    init(); 
    ... 
} 

void Object::init() 
{ 
//intialization stuff 
    ... 
} 
+1

Điều này tất nhiên không hoạt động nếu các thành viên là const. – Eclipse

+3

Điều này có nghĩa là bạn không khởi tạo, bạn đang khởi tạo sau. Điều này có thể được chấp nhận, nhưng thường là chậm hơn, và, khi bạn có các thành viên không có một ctor mặc định, không thể. –

+0

Đồng ý, chậm. Nhưng tránh việc phân chia mã. –

5

Có, đó là có thể.
Để đơn giản tôi sẽ giả vờ rằng mã gốc là:

class Foo { 
public: 
    Foo() : a(0), b(1), x() { } 
    Foo(int x) : a(0), b(1), x(x) { } 

    int get_a() const { return a; } 
    int get_b() const { return b; } 
    int get_x() const { return x; } 
private: 
    int a, b, x; 
}; 

Mã refactored, sau đó, là:

class Foo { 
public: 
    Foo() : x() { } 
    Foo(int x) : x(x) { } 

    int get_a() const { return common.a; } 
    int get_b() const { return common.b; } 
    int get_x() const { return x; } 
private: 
    struct Common { 
     Common() : a(0), b(1) { } 
     int a, b; 
    } common; 
    int x; 
}; 
+0

Ngoại trừ việc bạn vừa mới không thể tham khảo thanh.a, bây giờ là bar.sub.a . –

+0

Tất nhiên, nhưng nó sẽ được đóng gói trong một accessor như Foo :: getA() vì vậy nó không phải là một vấn đề :) –

+0

Tôi chỉnh sửa để làm cho rằng một chút rõ ràng hơn. –

1

Trước hết, bạn sẽ có rò rỉ bộ nhớ nếu bạn don' t thực hiện xóa các đối tượng được phân bổ trong destructor. Vì vậy, bạn nên xác định destructor của bạn và xóa các đối tượng ở đó.

Nếu bạn thực sự cần phân bổ động các thành viên (tôi không khuyến khích nếu lớp này sở hữu đối tượng thành viên dữ liệu), bạn có thể có một phương thức riêng làm tất cả khởi tạo và bạn có thể gọi phương thức đó từ các nhà xây dựng của bạn.

+0

tất cả các giá trị của MemoryColum mới() đang đi vào con trỏ thông minh. Điều này sẽ tự động xóa chúng khi Object bị hủy. Về lý thuyết. –

0

Bạn có thực sự cần 5 nhà thầu khác nhau không?

Rất thường xuyên nếu bạn cần các nhà xây dựng chuyển đổi, bạn cũng không muốn một hàm tạo mặc định.

Các nhà thầu khác của bạn tất cả chỉ kết hợp khác nhau của cùng một thứ. Nó có thể là một ý tưởng tốt hơn để chỉ có một hàm tạo có tất cả các tham số, với một tùy chọn cho mỗi tham số cho biết mong muốn gọi một kiểu mặc định nào đó cho tham số đó.

Ví dụ:

class Object 
{ 
    Object (const string *Name, // can be NULL 
    const string *path, // can be NULL 
    const bool loadMetadata); 

}; 
+0

Lấy NULL con trỏ-to-string sẽ rất bất ngờ, nhưng như là một phương sách cuối cùng nó hoàn toàn khả thi, có. –

1
Object (const string &Name = "", const string &path = "", const bool loadMetadata = false); 

này sẽ không giải quyết tất cả các vấn đề của mình (đặc biệt là không có cách nào để đại diện cho các nhà xây dựng với Tên và loadMetaData), nhưng nó sẽ ít nhất là sụp đổ một số của các nhà thầu thành một.

1

Tôi sẽ nói trước điều này bằng cách nói rằng tôi (rõ ràng) không biết chi tiết về hệ thống của bạn hoặc các ràng buộc dẫn đến quyết định thiết kế của bạn.

Điều đó đang được nói, một nguyên tắc tốt là khi một lớp bắt đầu trở nên bất tiện - khoảng thời gian bạn bắt đầu đặt câu hỏi về cách xử lý :) - đó có thể là thời gian để cấu trúc lại lớp đó thành một vài các lớp con nhỏ hơn.

Hãy nhớ rằng lớp học phải làm một điều rất tốt. Nếu bạn bắt đầu có các lớp học lớn cố gắng làm quá nhiều thứ, bạn sẽ không còn thiết kế OO tốt.

0

Chỉ cần đặt danh sách trình khởi tạo bên trong một MACRO và được thực hiện với nó.

1

Tôi sẽ sử dụng các phương pháp nhà máy khác nhau (phương pháp tĩnh) sẽ trả về một ptr thông minh cho lớp học của bạn. Tên phương thức của nhà máy cũng sẽ giúp tài liệu TẠI SAO bạn cần tất cả các tham số khác nhau.

+0

+1, tôi nghĩ đó là một ý tưởng hay, nhưng tôi không thấy nó như thế nào xung quanh những vấn đề được đề cập bởi Iraimbilanja - cụ thể là, làm thế nào để khởi tạo các thành viên const và tham chiếu. Bạn vẫn cần một loạt các nhà xây dựng nếu bạn muốn cung cấp các giá trị mặc định cho chúng. –

9

Bây giờ một vài năm sau đó chúng ta có C++ 11. Nếu bạn có thể sử dụng nó trong dự án của bạn, bạn có hai lựa chọn:

Khi các giá trị khởi tạo thông thường chỉ được biết đến trong thời gian chạy, bạn có thể sử dụng ủy thác nhà xây dựng, có nghĩa là một hàm tạo khác gọi.

// function that gives us the init value at runtime. 
int getInitValue(); 

class Foo 
{ 
    const int constant; 
    int userSet; 

public: 
    // initialize long member list with runtime values 
    Foo() 
     : constant(getInitValue()) 
     , userSet(getInitValue()) 
    {} 

    // other constructors with arguments 
    Foo(int userSetArg) 
     : Foo() 
    { 
     userSet = userSetArg; 
    } 
}; 

hoặc bạn có thể khởi tạo trực tiếp thành viên trong định nghĩa lớp nếu giá trị của chúng được biết lúc biên dịch.

class Foo 
{ 
    const int constant = 0; 
    int userSet = 0; 

public: 
    Foo(int userSetArg) : userSet(userSetArg){} 
} 
Các vấn đề liên quan