2011-09-07 44 views
5

Với các lớp dưới đây nằm trong hai tập tin tiêu đề riêng biệt và có thể xuất hiện trong bất kỳ thứ tự:C++ lớp mẫu xung đột

//TestB.h 
class TestB; //Forward declaration for a later operator= in a centralised header 

class TestA 
{ 
    const TestA&operator=(const TestB); //defined in Test.h 
}; 

Và:

//TestA.h 
class TestA; //Forward declaration for a later operator= in a centralised heaer 

class TestB 
{ 
    const TestB&operator=(const TestA); //defined in Test.h 
}; 

Làm thế nào để tránh những xung đột nguyên mẫu?

Trợ giúp được đánh giá cao.

Tôi hoàn toàn xin lỗi tất cả mọi người! Tôi đã có ý định cho có được tài liệu tham khảo (ký hiệu trong toán tử = đối số - Tôi sẽ không bao giờ vượt qua bằng cách sao chép thanh POD đơn giản) và có nghĩa là câu hỏi chỉ về prototyping xung đột! Tôi đoán nó sẽ cho thấy tầm quan trọng của việc đọc bằng chứng! Tôi đã chấp nhận câu trả lời cho bối cảnh ban đầu (sai lầm của tôi).

Tôi chỉ đơn thuần là chỉ quay lại trong vài phút và không nhận biết được lỗi!

+1

Ý bạn là "xung đột nguyên mẫu"? –

+2

Không có gì sai ở đây - điều này sẽ làm việc tốt. Bạn sẽ cần phải giải thích các lỗi tốt hơn, và với mã thực. –

+1

@Als: bạn có thể khai báo các hàm lấy các loại không đầy đủ theo giá trị; bạn chỉ không thể xác định hoặc gọi chúng cho đến sau định nghĩa của loại. –

Trả lời

4

Bạn chuyển tham chiếu đến các lớp dưới dạng tham số. Bằng cách này, một lớp và các hàm thành viên của nó có thể được khai báo mà không cần biết về các hàm khác.

//TestB.h 
class TestB; //Forward declaration for a later operator= in a centralised header 

class TestA 
{ 
    const TestA&operator=(const TestB &); //defined in TestB.h 
}; 

Và:

//TestA.h 
class TestA; //Forward declaration for a later operator= in a centralised heaer 

class TestB 
{ 
    const TestB&operator=(const TestA *); //defined in TestA.h 
}; 

Sau đó, bạn sẽ phải bao gồm cả TestA.h và TestB.h trong cả hai tập tin TestA.cpp và TestB.cpp để có thể xác định các thành viên chức năng.

+0

Không cần thay đổi ngữ nghĩa, bạn ** có thể ** khai báo các hàm lấy hoặc trả về * loại không đầy đủ * theo giá trị miễn là bạn không cố gắng * xác định * hoặc * gọi * những chức năng đó. –

4

Câu trả lời ban đầu của tôi có vẻ hoàn toàn sai.

Kiểm tra xem bạn có bao gồm bảo vệ trong tất cả các tệp tiêu đề của mình để bạn không kết thúc bằng chuỗi bao gồm vô hạn hay không. Sau đó bao gồm các tiêu đề trong mỗi thực hiện:

// A.cpp 
#include "A.h" 
#include "B.h" // for complete type TestB 

const TestA & TestA::operator=(const TestB) { /* ... */ } 

// B.cpp 
#include "B.h" 
#include "A.h" // for complete type TestA 

const TestB & TestB::operator=(const TestA) { /* ... */ } 

Xin lưu ý rằng một công trình như vậy sẽ tạo ra tình hình thiết kế tò mò nơi bất kỳ người tiêu dùng của một trong hai TestA hoặc TestB người muốn gọi các nhà điều hành phải luôn luôn bao gồm cả hai số A.hB.h, rất chi tiết, nhưng cũng có một chút bất ngờ. Nó có thể là đáng giá thêm một tập tin tiêu đề trung gian để sử dụng bởi khách hàng trong đó bao gồm cả hai tập tin tiêu đề, hoặc để thêm các vùi lẫn nhau (với bảo vệ!) Cho các tập tin tiêu đề chính mình.


Bạn không thể giải quyết này theo cách bạn đã viết nó, bởi vì bạn có một sự phụ thuộc vô hạn đệ quy phẳng ra.

Cách bạn thường làm điều này là để vượt qua các đối số bằng cách tham khảo chứ không phải bằng cách sao chép, kể từ khi đi ngang qua tham khảo không không đòi hỏi kiến ​​thức của các loại hoàn chỉnh:

const TestA & operator=(const TestB &); 
            ^^^^^ 
            vvvvv 
const TestB & operator=(const TestA &); 

+2

Bạn có thể khai báo các hàm lấy đối số theo giá trị ngay cả khi loại đối số không đầy đủ. Bạn chỉ không thể định nghĩa hàm cho đến sau định nghĩa lớp. –

+0

@Mike: Tôi rất ngạc nhiên, tôi thực sự nghĩ rằng điều đó không được phép! –

+0

@Kerrek SB: Bạn thậm chí không cần thêm cyclic bao gồm miễn là các hàm không * được định nghĩa * hoặc * được gọi * trong tệp tiêu đề và đơn vị dịch mà * gọi * hoặc * define * những thành viên đó chức năng bao gồm cả hai tiêu đề. –

0

Các khai báo chuyển tiếp chỉ tốt cho việc khai báo con trỏ và tham chiếu đến kiểu ... chúng không thể được sử dụng cho các thao tác sao chép thực tế cũng như khai báo các cá thể lớp như các thành phần dữ liệu lớp không tĩnh. Nếu bạn cố gắng làm như vậy, trình biên dịch sẽ cung cấp cho bạn một lỗi cho một kiểu không đầy đủ, vì không có đủ thông tin để cung cấp cho trình biên dịch thông tin về loại thực tế bao gồm (nghĩa là nó lớn bao nhiêu, v.v.), để nó có thể xây dựng lớp chính hoặc xác định bao nhiêu không gian ngăn xếp để phân bổ cho một trong các đối số phương thức của lớp.

1

Không có vấn đề thực tế trong các tiêu đề đó, miễn là chúng chỉ khai báo các chức năng thành viên và không cung cấp định nghĩa. Nghĩa là, nếu các định nghĩa cho các hàm thành viên nằm trong tệp .cpp bao gồm cả hai tiêu đề và không có cuộc gọi đến một trong hai hàm trong tiêu đề, thì nó sẽ hoạt động hoàn hảo.

Có một quan niệm sai lầm phổ biến mà bạn không thể sử dụng bất cứ điều gì đó trông giống như giá trị với một loại về phía trước tuyên bố, thực tế là bạn không có thể tạo đối tượng thuộc loại đó hoặc tạo ra các biến thành viên của kiểu đó, nhưng bạn có thể khai báo các hàm lấy hoặc trả lại các loại theo giá trị.

Bạn có thể không, mặt khác, xác định hoặc gọi những chức năng, vì đó sẽ đòi hỏi sự sáng tạo của một đối tượng của không đầy đủ loại.