2013-04-03 14 views
6

Tôi có hai lớp, với hai lớp toàn cầu friend oparator<< s.Nhà khai thác và đa hình toàn cầu

class A { 
    friend std::ostream& operator<<(std::ostream& o, const A &a); 
}; 

class B: public A { 
    friend std::ostream& operator<<(std::ostream& o, const B &b); 
}; 

Nếu tôi sử dụng nó như thế này, mọi thứ đang làm việc tốt, phiên bản B của người điều khiển được gọi là:

B b; 
std::cout << b; 

Nhưng nếu tôi sử dụng polymorpism, phiên bản A được gọi là, mặc dù động loại là B:

A* b = new B(); 
std::cout << *b; 

Một giải pháp được đúc:

std::cout << static_cast<B&>(*b); 

nhưng có giải pháp đơn giản hay thanh lịch hơn cho việc này không?

+2

Điều gì về việc gọi hàm ảo trong toán tử '<< (std :: ostream &, A const &)'? – dyp

Trả lời

10

Có. Một toán tử đầu ra và hàm virtual print trong các lớp.

class A 
{ 
public: 
    virtual ~A() {} 
private: 
    virtual void print(std::ostream&) {} 
    friend std::ostream& operator << (std::ostream& os, const A& obj) 
    { 
     obj.print(os); 
     return os; 
    } 
}; 

class B 
{ 
private: 
    virtual void print(std::ostream&) {} 
}; 

Live example

+0

vui lòng thêm 'return os;' vào hàm người bạn. – scones

+1

vui lòng xóa 'người bạn' khỏi chức năng đó - nó không cần phải là một người bạn nhưng có thể là một chức năng miễn phí;) –

+0

@ArneMertz in là riêng tư. Toán tử << có thể là hàm miễn phí trong trường hợp này như thế nào? Và tại sao chúng ta cần in ở nơi công cộng? – ForEveR

3

Các phiên bản của các chức năng trong các lớp thừa kế chỉ được gọi khi bạn truy cập chúng thông qua một con trỏ tới lớp cơ sở nếu bạn định nghĩa chúng như virtual vì trình biên dịch không có một đầu mối những gì các lớp đối tượng được trỏ đến bởi con trỏ thực sự là. Ở đây rắc rối là bạn đang xác định các chức năng của bạn để chúng không thể là ảo, giải pháp rất đơn giản: việc thực hiện chức năng operator<< của A gọi là A mà sau đó bạn có thể quá tải trong B.

2

Đơn giản chỉ cần không sử dụng bạn bè. Họ là một khớp nối chặt chẽ hơn so với thừa kế. Đặc biệt là nếu bạn viết những toán tử bạn bè đó cho một lớp mẫu, người ta có thể chuyên về nó và có quyền truy cập hợp pháp vào nội bộ của lớp của bạn. Vì những lý do đó, tôi chỉ sử dụng các toán tử đó làm đường cú pháp và cho phép chúng ủy quyền cho các hàm thành viên thực hiện công việc thực. Bằng cách đó giải pháp cho vấn đề của bạn là không có trí tuệ:

class A { 
public: 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

std::ostream& operator<<(std::ostream& os, A const& a) 
{ return a.printToStream(os); } 

class B: public A { 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

có một lớp C có nguồn gốc từ A? Không vấn đề gì, không cần phải xác định đường cú pháp (tức là operator<<) một lần nữa, chỉ cần xác định cách thực hiện công việc thực tế, tức là ghi đè printToStream - đó là lý do tại sao tôi làm cho nó thành ảo.