2009-06-15 32 views
9

Vì vậy, tôi đã viết một số mã, và tôi đã có một cái gì đó như thế này:Có cách nào đúng để trả về một cá thể đối tượng mới bằng cách tham chiếu trong C++ không?

class Box 
{ 
    private: 
    float x, y, w, h; 

    public: 
    //... 
    Rectangle & GetRect(void) const 
    { 
     return Rectangle(x, y, w, h); 
    } 
}; 

Rồi sau này trong một số mã:

Rectangle rect = theBox.GetRect(); 

nào làm việc trong debug build của tôi, nhưng trong phiên bản đã có "vấn đề "trả về hình chữ nhật đó bằng cách tham khảo - về cơ bản tôi có hình chữ nhật không được khởi tạo. Lớp Rectangle có toán tử = và một hàm tạo bản sao. Mà không nhận được vào lý do tại sao điều này đã phá vỡ, tôi thực sự quan tâm nhiều hơn trong cách chính xác để trả lại một đối tượng (mới) bằng cách tham khảo với mục đích gán sao chép vào một biến. Tôi chỉ là ngớ ngẩn? Nếu nó không được thực hiện? Tôi biết tôi có thể trở lại một con trỏ và sau đó dereference về chuyển nhượng, nhưng tôi không muốn. Một số phần của tôi cảm thấy như trở về bởi giá trị sẽ dẫn đến sao chép dự phòng của đối tượng - không phải là con số biên dịch ra và tối ưu hóa nó?

Dường như đây là một câu hỏi nhỏ. Tôi cảm thấy gần như xấu hổ Tôi không biết điều này sau nhiều năm mã hóa C++ vì vậy hy vọng ai đó có thể xóa điều này cho tôi. :)

+0

Lưu ý rằng không có chuyển nhượng trong mã của bạn. –

Trả lời

21

Bạn không thể trả lại tham chiếu đến đối tượng tạm thời trên ngăn xếp. Bạn có ba lựa chọn:

  1. Return nó bằng giá trị
  2. Return bằng cách tham khảo qua một con trỏ đến một cái gì đó mà bạn đã tạo trên heap với các nhà điều hành mới.
  3. Quay lại theo tham chiếu những gì bạn nhận được bằng tham chiếu dưới dạng đối số. [CHỈNH SỬA: Nhờ @ harshath.jr đã chỉ ra điều này]

Lưu ý rằng khi bạn trả về giá trị như trong đoạn mã dưới đây, trình biên dịch sẽ tối ưu hóa nhiệm vụ để tránh sao chép - tức là nó sẽ tạo Hình chữ nhật đơn (rect) bằng cách tối ưu hóa việc tạo + gán + sao chép vào một tạo. Điều này chỉ hoạt động khi bạn tạo đối tượng mới khi trở về từ hàm.

Rectangle GetRect(void) const 
{ 
    return Rectangle(x, y, w, h); 
} 

Rectangle rect = theBox.GetRect(); 
+5

Một lần nữa, không có bài tập nào trong mã này. –

+3

Tùy chọn 3: trả về bằng tham chiếu những gì bạn đã nhận được dưới dạng tranh luận bằng cách tham chiếu.Điều này đặc biệt được sử dụng trong khi các toán tử ghi đè. – jrharshath

+0

ví dụ về con trỏ sẽ đã tốt trong câu trả lời này như Rectangle * GetRect (void) const { trở Rectangle mới (x, y, w, h) } Rectangle * rect = theBox.GetRect() ; sau xóa trực tiếp; – AppDeveloper

15

Không, bạn không thể thực hiện việc này. Về cơ bản những gì bạn đang cố gắng làm trong mẫu này là trả về một tham chiếu đến một biến tạm thời trên ngăn xếp. Khi tham chiếu được trả lại, biến nó trỏ tới sẽ bị hủy và do đó tham chiếu không hợp lệ.

+0

Vì vậy, bạn đang nói nếu đó là const Rectangle & GetRect (...) nó sẽ là chính xác? –

+0

Vấn đề không phải là constness mà đúng hơn là thứ mà bạn có tham chiếu được tạo ra trên stack. –

+0

@pbhogan, không. Tôi không nên thêm từ const vào câu trả lời của tôi. Đó là một sai lầm về phía tôi. Bạn chỉ cần trả lại giá trị này ở đây. – JaredPar

2
  • Hoặc trả về một tham chiếu đến các bộ phận bên trong lớp Box của bạn (có thành viên Rectangle. Trả về một tài liệu tham khảo const được thông báo).
  • hoặc chỉ trả lại Rectangle. Lưu ý rằng việc sử dụng thành ngữ return SomeClass(a,b,c); có thể sẽ kích hoạt một return value optimization (RVO) trên trình biên dịch tốt.

Kiểm tra triển khai std::complex để biết chi tiết.

2

Bạn có thể bị nhầm lẫn bởi khái niệm về tuổi thọ của tạm thời. Xem xét:

void f1(const A & a) { 
} 

A f2() { 
    return A; 
} 

f1(f2()); 

Đây là mã OK, và tiêu chuẩn nói rằng không tên tạm thời mà f2 tạo phải treo tròn đủ lâu để có thể sử dụng được trong f1.

Tuy nhiên, trường hợp của bạn hơi khác. Thứ mà hàm của bạn trả về là một tham chiếu, và do đó tạm thời không tên cũng là một tham chiếu. Đó là tham chiếu phải treo vòng đủ dài để có ích, nhưng điều nó đề cập đến không cần.

+0

Bạn nói đúng. Tôi đã dành quá nhiều thời gian vào các ngôn ngữ động - tôi bắt đầu kỳ vọng toàn bộ thời gian tạm thời tuân theo tham chiếu. –

1

Điều này là không thể. Tham chiếu là một dạng khác của một con trỏ và thực tế bạn trả về một địa chỉ của một đối tượng sẽ bị hủy (destructor) và thậm chí có thể bị ghi đè bởi thời gian người gọi nhận điều khiển.

Bạn có thể

  • cuộc gọi mới và trả về một con trỏ (có lẽ bạn nên nghĩ đến một con trỏ thông minh) cho một đối tượng đống phân bổ hoặc
  • trở lại theo giá trị hoặc
  • vượt qua đối tượng bằng tham chiếu vào một hàm để nó lấp đầy nó.
7

Trả về một đối tượng theo giá trị (xem ví dụ bên dưới) có thể thực sự rẻ hơn bạn nghĩ. Trình biên dịch thường tối ưu hóa bản sao phụ. Đây được gọi là return value optimization.

Rectangle GetRect(void) const 
    { 
      return Rectangle(x, y, w, h); 
    } 
+0

+1 để đặt tên cho tối ưu hóa được phép cụ thể –

0

Nếu Bitwise hình chữ nhật trông giống như Box tức là bao gồm bốn nổi (nhưng có chức năng thành viên khác nhau), bạn có thể sử dụng một reinterpret_cast, mặc dù tôi sẽ không ở tất cả khuyên nó:

const Rectangle & GetRect(void) const 
    { 
      assert(sizeof(Rectangle) == sizeof(Box)); 
      return reinterpret_cast <Rectangle> (*this); 
    } 
0

chúng ta có thể sử dụng auto_ptr, nếu chúng ta muốn sử dụng mới và an toàn từ Memory Leak

class Box { 

    private: float x, y, w, h; 

    public:  

    //...  

    std::auto_ptr<Rectangle> GetRect(void) const 
    {   
     return std::auto_ptr<Rectangle> (new Rectangle(x, y, w, h)); 
    } 

}; 
+0

sử dụng shared_ptr ngày nay. – gbjbaanb

2

Có cách nào đúng để trả về một cá thể đối tượng mới bằng cách tham chiếu trong C++ không?

Không, không phải theo tham chiếu. Có hai cách để tạo ra một đối tượng mới:

Trên stack:

Rectangle makeRect() 
{ 
    return Rectangle(x, y, w, h); 
} 
Rectangle r = makeRect(); // return by value 

Trên đống:

Rectangle * makeRect() 
{ 
    return new Rectangle(x, y, w, y); 
} 
Rectangle * r = makeRect(); // returned a pointer, don't forget to delete it later 

Tại sao không phải cái gì như thế này?

class Box 
{ 
    private: 
    Rectangle mRectangle; 

    public: 
    Box(float x, float y, float w, float h) : 
     mRectangle(x, y, w, h) // Forgive me for making assumptions 
          // about the inner workings of your 
          // code here. 
    { 
    } 

    const Rectangle & GetRect() const 
    { 
     return mRectangle; 
    } 
}; 

Rectangle rect = theBox.GetRect(); 

'Bài tập' sẽ hoạt động ngay bây giờ. (Về mặt kỹ thuật, đây không phải là toán tử gán, mà là một hàm tạo bản sao đang được gọi.)

Hy vọng trợ giúp

+0

Bản tóm tắt tốt về các tùy chọn của tôi, cảm ơn bạn. Tôi thực hiện tùy chọn cuối cùng của bạn bất cứ khi nào có thể, tuy nhiên ví dụ của tôi ở đây là hơi đơn giản. Hộp thực sự là một cái gì đó phức tạp hơn và GetRect trả về một hình chữ nhật được tính toán. Dù sao, lựa chọn đầu tiên của bạn là những gì tôi nên làm ... Tôi chỉ cố gắng thông minh hơn trình biên dịch tôi đoán: p –

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