2010-01-13 25 views
23

(chỉnh sửa từ bài gốc để thay đổi "BaseMessage" thành "BaseMessage const &")C++ Tóm tắt điều hành lớp học quá tải và thực thi giao diện câu hỏi

Hello All, Tôi rất mới để C++, vì vậy tôi hy vọng bạn folks có thể giúp tôi "xem các lỗi của cách của tôi".

Tôi có một hệ thống phân cấp thư và tôi đang cố gắng sử dụng lớp cơ sở trừu tượng để thực thi một giao diện. Đặc biệt, tôi muốn ép buộc từng thông điệp có nguồn gốc để cung cấp một toán tử quá tải < <.

Khi tôi cố gắng làm điều này với một cái gì đó như thế này:

class BaseMessage 
{ 
public: 

// some non-pure virtual function declarations 
// some pure virtual function declarations 

virtual ostream& operator<<(ostream& stream, const BaseMessage& objectArg) = 0; 

} 

trình biên dịch phàn nàn rằng

"lỗi: không thể khai báo tham số 'objectArg' là loại trừu tượng 'BaseMessage'

Tôi tin rằng cũng có những vấn đề "bạn bè" liên quan ở đây, nhưng khi tôi cố gắng tuyên bố nó là:

virtual friend ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

trình biên dịch thêm một lỗi Ngoài

"lỗi: chức năng ảo không thể làm bạn"

Có cách nào để đảm bảo rằng tất cả các nguồn gốc (message) lớp học của tôi cung cấp một "< <" hành ostream ?

Cảm ơn nhiều,

Steve

+0

Câu trả lời của Nilolai là giải pháp tốt nhất cho những gì bạn đang cố gắng hoàn thành. Tuy nhiên, lỗi cụ thể bạn nhận được là vì bạn đang cố chuyển đối tượng BaseMessage theo giá trị (đối số thứ hai cho toán tử ảo của bạn <<). Điều này không thể làm việc vì BaseMessage bao gồm một hàm ảo thuần túy (cùng một toán tử ảo <<), vì vậy không thể xây dựng một cá thể của một BaseMessage để truyền theo giá trị. Lưu ý rằng phiên bản của toán tử Nilolai << lấy đối số thứ hai của nó bằng tham chiếu (nó sẽ là một lớp có nguồn gốc từ Base). –

Trả lời

40

Các quy ước chung cho việc này là phải có một nhà điều hành friend đầu ra ở cấp cơ sở và có nó gọi hàm ảo riêng:

class Base 
{ 
public: 

    /// don't forget this 
    virtual ~Base(); 

    /// std stream interface 
    friend std::ostream& operator<<(std::ostream& out, const Base& b) 
    { 
     b.Print(out); 
     return out; 
    } 

private: 

    /// derivation interface 
    virtual void Print(std::ostream&) const =0; 
}; 
+0

Đây là giải pháp làm việc duy nhất tôi đã thấy trên trang ở đây: mọi người khác đang nói chuyện với họ: (hãy thử biên dịch mã người của bạn!) +1 – jkp

+0

Điều này rất hữu ích, cảm ơn Nikolai Tôi có thể thấy nhiều nơi để sử dụng mẫu. - Steve –

+0

Tôi không biết bạn có thể định nghĩa hàm người bạn trực tiếp bên trong khai báo lớp như thế. Tốt để biết!Sách giáo khoa sẽ luôn đặt tuyên bố bạn bè bên trong và định nghĩa bên ngoài. Việc định nghĩa trực tiếp bên trong một lớp cũng làm việc với một hàm 'swap' bạn bè? –

3

lớp trừu tượng không thể được khởi tạo, vì vậy làm điều này:

virtual ostream& operator<<(ostream& stream, const Base &objectArg) = 0; 

chức năng ảo phải có chức năng thành viên dụ trong khi hàm bạn bè là chức năng không thành viên , do đó, nó không thể được khai báo là ảo.

Chức năng tĩnh tương tự không thể ảo vì phương thức của lớp không phải là phương thức thể hiện.

Đề xuất của tôi là:

class Base { 
public: 
virtual ostream& print (ostream& stream) const = 0; 
}; 


class Derived :public Base { 
public: 
virtual ostream& print (ostream& stream) const { //do something } 
}; 

ostream& operator <<(ostream& stream, const BaseMessage &objectArg) 
{ 
    return objectArg.print(stream); 
} 
0

Vấn đề ở đây là "BaseMessage objectArg" đang nói rằng đối tượng objectArg nên được truyền theo giá trị.

Điều này là không thể vì bạn đã làm cho lớp trừu tượng với cuộc gọi ảo thuần túy của bạn. Một vượt qua bằng cách tham chiếu "BaseMessage & objectArg" hoặc vượt qua bằng con trỏ "BaseMessage * objectArg" sẽ làm cho lỗi này biến mất.

Truyền theo giá trị nghĩa là nó tạo một phiên bản mới của biến bằng cách sử dụng hàm tạo bản sao. Vì bạn đã đảm bảo rằng không thể tạo một cá thể của BaseMessage, điều này không thể thực hiện được.

3

Các nhà khai thác dòng như:

virtual ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0; 

chỉ đơn giản là không thể các hàm thành viên và do đó không thể là các hàm ảo. Lý do cho điều này là khi bạn nói điều gì đó như:

a << b; 

bạn đang thực sự nói

a.operator<<(b); 

Trong trường hợp này một là một dòng suối, không phải là một thể hiện của lớp học của bạn, vì vậy các nhà điều hành không thể một thành viên trong lớp của bạn. Bạn nên thường làm cho nó thành một hàm miễn phí (không phải thành viên), truy cập cá thể lớp của bạn thông qua các hàm thành viên phù hợp.

1

Tuyên bố dành cho toán tử < <() không đúng. Đối với phiên bản nhị phân của op < <, bạn không cần phải khai báo các tham số thứ hai - nó được giả định là this nếu op < < là một hàm thành viên của lớp:

virtual ostream& operator<<(ostream& stream) = 0;

Hơn nữa, như được đề cập bởi những người khác, các toán tử chèn luồng phải là các hàm toàn cục. Làm cho họ thành viên chức năng chỉ sẽ không hoạt động.

Cũng không phải thứ gì đó không liên quan đến câu hỏi của bạn, mà là một vấn đề không nontheless. Trong triển khai ban đầu của bạn, bạn đã chuyển một đối tượng lớp cơ sở trừu tượng theo giá trị, thay vì bằng tham chiếu hoặc bằng con trỏ. Khi bạn làm điều này, bạn "cắt đối tượng". Ý định của bạn, tôi chắc chắn, đã vượt qua một con trỏ lớp cơ sở đến một loại đa hình cho hàm, và sau đó có các phương thức gọi hàm đa hình. Ví dụ, bạn đang cố gắng làm điều gì đó tương tự như sau:

#include <cstdio> 
#include <string> 
#include <iostream> 
using namespace std; 

class Base 
{ 
public: 
    virtual void dump() 
    { 
     cout << "Base"; 
    }; 
}; 

class Der : public Base 
{ 
public: 
    void dump() 
    { 
     cout << "Der"; 
    }; 
}; 

void DumpIt(Base b) 
{ 
    b.dump(); 
} 


int main() 
{ 
    Der obj; 
    DumpIt(obj); 
    return 0; 

} 

... và chờ đợi kết quả là "Der" Nhưng trên thực tế đầu ra là "cơ sở" vì Object Slicing. Bởi vì hàm DumpIt() lấy đối tượng Base theo giá trị, một đối tượng Base tạm thời mới được tạo dựa trên bản gốc. Để có được chức năng bạn mong đợi, bạn cần phải chuyển qua tham chiếu hoặc theo con trỏ:

void DumpIt(Base & b) 
{ 
    b.dump(); 
} 

Đầu ra từ chức năng này là "Der".

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