2013-01-23 38 views
5

Khi tôi hiểu thời gian chờ, mã sau sẽ hoạt động, nhưng không.C++ tạm thời - "phương pháp ảo thuần túy được gọi là"

struct base 
{ 
    virtual~base() {} 
    virtual void virt()const=0; 
}; 
struct derived:public base 
{ 
    virtual void virt()const {} 
}; 

const base& foo() {return derived();} 

int main() 
{ 
    foo().virt(); 
    return 0; 
} 

Lệnh gọi hàm virt() đưa ra lỗi "gọi hàm ảo thuần túy". Tại sao vậy, và tôi nên làm gì?

Trả lời

5

Dường như bạn đang mong đợi tham chiếu const để kéo dài tuổi thọ của tạm thời. Có một số tình huống mà điều này không xảy ra. Một trong những tình huống đó là khi trả lại tạm thời:

Ngữ cảnh thứ hai [trong đó thời gian bị hủy tại điểm khác với kết thúc toàn bộ biểu thức] là khi tham chiếu bị ràng buộc tạm thời. Tạm thời mà tham chiếu được đóng thành quyển hoặc tạm thời mà là đối tượng hoàn toàn của một subobject mà tham chiếu được ràng buộc kéo dài tuổi thọ của các tài liệu tham khảo trừ:

[...]

  • Các tuổi thọ của một giới hạn tạm thời với giá trị trả về trong câu lệnh trả về hàm (6.6.3) không được kéo dài; tạm thời bị hủy ở cuối biểu thức đầy đủ trong câu lệnh trả về.

Kể từ khi gọi một hàm thành viên của đối tượng được trả về bởi foo() sẽ đòi hỏi một giá trị trái-to-rvalue chuyển đổi và đối tượng không hợp lệ (không bắt nguồn từ loại base), bạn sẽ có được hành vi undefined:

Nếu đối tượng mà glvalue đề cập không phải là đối tượng thuộc loại T và không phải là đối tượng thuộc loại bắt nguồn từ T hoặc nếu đối tượng chưa được khởi tạo, chương trình yêu cầu chuyển đổi này có hành vi không xác định.

8

Bạn đang trả về tham chiếu đến tạm thời bị hủy khi hàm kết thúc ở cuối câu hỏi return và bạn nhận được hành vi không xác định.

Bạn không thể trả lại bất kỳ loại tham chiếu nào thành tạm thời và nếu bạn trả về base theo giá trị bạn sẽ bị cắt, vì vậy nếu bạn thực sự muốn điều này hoạt động, bạn nên trả lại std::unique_ptr<base>.

+0

chính xác, vì bảng ảo cũng sẽ được dọn dẹp ... do đó là thông báo. –

+2

@DougT .: Không có "vì" trong "hành vi không xác định". (Ngoài ra, các bảng * ảo * không bao giờ bị "dọn dẹp"). –

+0

Nhưng không nên để đối tượng tạm thời bị phá hủy vào cuối biểu thức đầy đủ, đó là sau khi virt() đã trở lại? – Dave

3

Tôi không nghĩ bạn có thể trả lại tham chiếu đến các đối tượng như vậy. Trường hợp derived() xuất hiện ngoài phạm vi ngay khi foo() trả về. Có

base *foo() { return new derived(); } 

foo()->virt(); 

nên làm việc.

+3

Điều này sẽ hiệu quả, nhưng chúng tôi không cần truyền con trỏ thô quanh những ngày này ... –

+2

chắc chắn, nếu bạn muốn sử dụng shared_ptr <> hoặc thích, đó là những gì bạn nên làm trong một mã thực. Tuy nhiên quan điểm của tôi về cơ bản là chúng ta cần phải phân bổ các đối tượng trên đống này để làm việc, tại sao ẩn thực tế đó dưới áo choàng của con trỏ-sợ hãi? – Johannes

+1

@Johannes: Bởi vì nó chỉ yêu cầu rò rỉ bộ nhớ. Con trỏ thông minh nằm trong thư viện chuẩn - sử dụng chúng! : -] – ildjarn

0

Không có đối tượng nào trong khi gọi, đối tượng địa phương đã bị xóa. Sẽ không có vtable để xem xét. Hãy thử:

base& foo() { return *new derived(); } 
+0

Người gọi biết anh ta phải giải phóng bộ nhớ như thế nào? –

0

Đối tượng tạm thời 'bắt nguồn()' của bạn được cấp phát trên ngăn xếp. Nó có thể có một cái gì đó để làm với các đối tượng up-casting.

const base & foo() {derived * d = new derived(); return * d; }

Làm việc tốt cho tôi.

+1

Bộ nhớ bị rò rỉ là _không hoạt động tốt. ; -] – ildjarn

+0

Chắc chắn- cảm ơn vì đã bắt. Tôi có thể đã sử dụng một tăng :: shared_ptr <> – Arcturus

0

Thay đổi mảnh vấn đề này:

class A { 
public: 
    const base &foo() { return d; } 
private: 
    derived d; 
}; 

Bằng cách này, tuổi thọ của các đối tượng có nguồn gốc là chừng nào cuộc đời của A, và sẽ không bao giờ có bất kỳ vấn đề.

+0

Nếu tôi xây dựng A trên stack của một hàm 'bar' và trả về tham chiếu đến' d' từ 'bar'? –

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