2010-04-08 32 views
14

tôi có mã trông như thế này:C++ liên tục suốt đời tài liệu tham khảo (adapter container)

class T {}; 

class container { 
const T &first, T &second; 
container(const T&first, const T & second); 
}; 

class adapter : T {}; 

container(adapter(), adapter()); 

Tôi nghĩ tuổi thọ của tài liệu tham khảo liên tục sẽ là cuộc đời của container. Tuy nhiên, nó xuất hiện nếu không, đối tượng adapter bị phá hủy sau khi container được tạo ra, để lại tham chiếu lơ lửng.

Vòng đời chính xác là gì?

là phạm vi ngăn chứa đối tượng tạm thời của bộ điều hợp phạm vi của đối tượng chứa hoặc của hàm tạo thùng chứa?

cách triển khai đúng đối tượng tạm thời ràng buộc với tham chiếu thành viên lớp học?

Cảm ơn

Trả lời

16

Theo tiêu chuẩn C++ 03, giới hạn tạm thời đối với tham chiếu có các thời gian khác nhau tùy thuộc vào ngữ cảnh. Trong ví dụ của bạn, tôi cho rằng phần được đánh dấu bên dưới được áp dụng (12.2/5 "Đối tượng tạm thời"):

Tạm thời tham chiếu bị ràng buộc hoặc tạm thời là đối tượng hoàn chỉnh đối với một đối tượng phụ tạm thời bị ràng buộc trong suốt thời gian tồn tại của tham chiếu trừ khi được chỉ định bên dưới. Một ràng buộc tạm thời cho một thành viên tham chiếu trong bộ khởi tạo của hàm khởi tạo (12.6.2) vẫn tồn tại cho đến khi hàm khởi tạo xuất hiện. Một giới hạn tạm thời với tham số tham chiếu trong một cuộc gọi hàm (5.2.2) vẫn tồn tại cho đến khi hoàn thành biểu thức đầy đủ có chứa cuộc gọi.

Vì vậy, trong khi ràng buộc tạm thời là một kỹ thuật tiên tiến để kéo dài tuổi thọ của đối tượng tạm thời (GotW #88: A Candidate For the "Most Important const"), nó dường như sẽ không giúp bạn trong trường hợp này. Mặt khác, Eric Niebler có một bài báo mà bạn có thể quan tâm thảo luận về một kỹ thuật thú vị (nếu phức tạp) có thể cho phép các nhà thầu của lớp suy luận liệu một đối tượng tạm thời (thực sự là một rvalue) đã được truyền cho nó hay không. (và do đó sẽ phải được sao chép) hoặc một tổ chức phi tạm thời (vế trái) như được thông qua (và do đó có thể có khả năng an toàn đã là một tài liệu tham khảo giấu đi thay vì sao chép):

Tốt may mắn với nó mặc dù - mỗi khi tôi đọc bài báo, tôi phải làm việc thông qua mọi thứ như thể tôi chưa từng thấy tài liệu trước đây. Nó chỉ dính vào tôi trong một khoảnh khắc thoáng qua ...

Và tôi nên đề cập đến các tham chiếu giá trị của C++ 0x nên làm cho các kỹ thuật của Niebler không cần thiết. Tài liệu tham khảo Rvalue sẽ được hỗ trợ bởi MSVC 2010 dự kiến ​​sẽ được phát hành trong một tuần hoặc lâu hơn (vào ngày 12 tháng 4 năm 2010 nếu tôi nhớ chính xác). Tôi không biết trạng thái của các tham chiếu rvalue là gì trong GCC.

+0

Tôi nghĩ rằng trong trường hợp này, tạm thời bị ràng buộc với tham số cuộc gọi hàm (gọi hàm dựng) như trong câu tiếp theo. Có, nó cũng bị ràng buộc với thành viên vì bí danh trong bộ khởi tạo ctor, và có, nó sẽ tồn tại cho đến khi các hàm khởi tạo (dài hơn, trên thực tế, nếu biểu thức đầy đủ chứa lời gọi hàm tạo cũng làm những việc khác). Nhưng tôi nghĩ đoạn văn được đánh dấu đề cập đến những thứ như 'struct container {const & adapter a; container(): a (adapter()) {}}; '. –

+0

@Steve: nhìn kỹ hơn, tôi nghĩ bạn nói đúng - Tôi sẽ cập nhật câu trả lời (cùng một kết quả). –

6

tài liệu tham khảo const tạm thời chỉ có thời gian tồn tại của báo cáo kết quả hiện tại (có nghĩa là, họ đi ra khỏi phạm vi ngay trước dấu chấm phẩy). Vì vậy, quy tắc của ngón tay cái không bao giờ dựa vào một tham chiếu const tồn tại ngoài vòng đời của hàm nhận nó như một tham số, trong trường hợp này chỉ là hàm tạo. Vì vậy, một khi các nhà xây dựng được thực hiện, không dựa vào bất kỳ tham chiếu const vẫn còn xung quanh.

Không có cách nào để thay đổi/ghi đè/kéo dài tuổi thọ này cho thời gian chờ. Nếu bạn muốn có một cuộc đời dài hơn, sử dụng một đối tượng thực tế và không phải là một tạm thời:

adapter a, b; 
container(a, b); // lifetime is the lifetime of a and b 

Hoặc tốt hơn, chỉ cần không sử dụng tài liệu tham khảo liên tục cho các thành viên lớp ngoại trừ trong các trường hợp nghiêm trọng nhất khi các đối tượng này rất liên quan chặt chẽ và chắc chắn không phải tạm thời.

+2

Để được chính xác hơn, họ sống cho đến cuối của biểu thức đầy đủ họ đã tạo ra trong – GManNickG

+1

"Không có cách nào để thay đổi/ghi đè/kéo dài tuổi thọ này cho là tạm thời." - thực sự có nghĩa là, nó chỉ là không hữu ích trong các trường hợp như thế này. Nếu bạn sử dụng tạm thời để khởi tạo tham chiếu const với thời lượng tự động, thì thời gian tồn tại tạm thời được kéo dài cho đến khi phạm vi tự động bị thoát. –

1

Tham chiếu sẽ tồn tại trong toàn bộ vòng đời container, nhưng đối tượng đang được tham chiếu sẽ chỉ tồn tại trong suốt thời gian tồn tại của đối tượng đó. Trong trường hợp này, bạn đã ràng buộc tham chiếu đến một đối tượng tạm thời với phân bổ lưu trữ tự động ("phân bổ ngăn xếp", nếu bạn sẽ, mặc dù đó không phải là danh mục C++). Do đó, bạn không thể mong đợi tạm thời tồn tại ngoài tuyên bố mà nó được viết (vì nó nằm ngoài phạm vi ngay lập tức sau khi gọi hàm tạo cho container). Cách tốt nhất để giải quyết vấn đề này là sử dụng bản sao thay vì tham chiếu. Vì bạn đang sử dụng tham chiếu const, dù sao, nó sẽ có ngữ nghĩa tương tự.

Bạn nên xác định lại lớp học của bạn như:

 
template<typename T> 
class container 
{ 
    public: 
     container(const T& first, const T& second) : first(first), second(second) {} 
    private: 
     const T first; 
     const T second; 
}; 

Ngoài ra, bạn có thể cung cấp cho đối tượng của bạn một tên để ngăn chặn chúng từ đi ra khỏi phạm vi:

 
    adaptor first; 
    adaptor second; 
    container c(first,second); 

Tuy nhiên, tôi không nghĩ rằng đây là một ý tưởng hay, vì một tuyên bố như return c không hợp lệ.

Sửa
Nếu mục tiêu của bạn là để chia sẻ đối tượng để tránh chi phí sao chép, sau đó bạn nên xem xét sử dụng đối tượng con trỏ thông minh.Ví dụ, chúng ta có thể xác định lại đối tượng của bạn sử dụng con trỏ thông minh như sau:

 
template<typename T> 
class container 
{ 
    public: 
     container(const boost::shared_ptr<const T>& first, const boost::shared_ptr<const T>& second) : first(first), second(second) {} 
    private: 
     boost::shared_ptr<const T> first; 
     boost::shared_ptr<const T> second; 
}; 

Sau đó bạn có thể sử dụng:

 
boost::shared_ptr<const adaptor> first(new adaptor); 
boost::shared_ptr<const adaptor> second(new adaptor); 
container<adaptor> c(first,second); 

Hoặc, nếu bạn muốn có bản sao có thể thay đổi các đầu tiên và thứ hai tại địa phương:

 
boost::shared_ptr<adaptor> first(new adaptor); 
boost::shared_ptr<adaptor> second(new adaptor); 
container<adaptor> c(boost::const_pointer_cast<const adaptor>(first),boost::const_pointer_cast<const adaptor>(second)); 
+0

các đối tượng thực sự là khá nặng với các nhà thầu tác dụng phụ. Tôi đang cố gắng tránh tạo bản sao. – Anycorn

+2

@aaa, trong trường hợp đó, bạn nên sử dụng các con trỏ thông minh như tăng :: shared_ptr. –

+0

Tôi nghĩ rằng làm điều đó, tuy nhiên lớp học là trong giao diện công cộng đang cố gắng để giữ tăng miễn phí – Anycorn

-1

Đừng làm điều này. Một tạm thời bị phá hủy ngay lập tức sau khi biểu thức được tạo ra (trừ trường hợp nó bị ràng buộc ngay lập tức với một tham chiếu, trong trường hợp đó là phạm vi của tham chiếu). Không thể kéo dài tuổi thọ của lớp đó.

Đây là lý do tại sao tôi không bao giờ lưu trữ thành viên dưới dạng tham chiếu - chỉ sao chép đối tượng hoặc con trỏ. Đối với tôi, con trỏ làm cho nó rõ ràng rằng cuộc đời đến để chơi. Đặc biệt trong trường hợp của một hàm tạo, không rõ ràng rằng các tham số hàm tạo của bạn phải vượt ra ngoài chính lớp đó.

+1

-1: Con trỏ nên được thay thế bằng tài liệu tham khảo bất cứ khi nào có thể. –

+0

Tôi đã không -1, nhưng họ sống cho đến khi kết thúc của biểu thức đầy đủ mà họ đã được tạo ra trong, không phạm vi. – GManNickG

+0

Trước hết, đó là một tuyên bố vô lý. Thứ hai, trong trường hợp này, các tham chiếu làm cho hành vi này hoàn toàn không rõ ràng. Lame -1. – Stephen

0

Nếu bạn muốn tránh sao chép, thì tôi cho rằng Vùng chứa phải tự tạo các cá thể được lưu trữ.

Nếu bạn muốn gọi hàm khởi tạo mặc định, thì sẽ không có vấn đề gì. Chỉ cần gọi hàm dựng mặc định của Container.

Có thể có vấn đề hơn nếu bạn muốn gọi hàm tạo không mặc định của loại được chứa. C++ 0x sẽ có giải pháp tốt hơn cho điều đó.

Là một bài tập, vùng chứa có thể chấp nhận T hoặc đối tượng chứa đối số cho hàm tạo T. Điều này vẫn dựa trên RVO (tối ưu hóa giá trị trả về).

template <class T1> 
class construct_with_1 
{ 
    T1 _1; 
public: 
    construct_with_1(const T1& t1): _1(t1) {} 
    template <class U> 
    U construct() const { return U(_1); } 
}; 

template <class T1, class T2> 
class construct_with_2 
{ 
    T1 _1; 
    T2 _2; 
public: 
    construct_with_2(const T1& t1, const T2& t2): _1(t1), _2(t2) {} 
    template <class U> 
    U construct() const { return U(_1, _2); } 
}; 

//etc for other arities 

template <class T1> 
construct_with_1<T1> construct_with(const T1& t1) 
{ 
    return construct_with_1<T1>(t1); 
} 

template <class T1, class T2> 
construct_with_2<T1, T2> construct_with(const T1& t1, const T2& t2) 
{ 
    return construct_with_2<T1, T2>(t1, t2); 
} 

//etc 
template <class T> 
T construct(const T& source) { return source; } 

template <class T, class T1> 
T construct(const construct_with_1<T1>& args) 
{ 
    return args.template construct<T>(); 
} 

template <class T, class T1, class T2> 
T construct(const construct_with_2<T1, T2>& args) 
{ 
    return args.template construct<T>(); 
} 

template <class T> 
class Container 
{ 
public: 
    T first, second; 

    template <class T1, class T2> 
    Container(const T1& a = T1(), const T2& b = T2()) : 
     first(construct<T>(a)), second(construct<T>(b)) {} 
}; 

#include <iostream> 

class Test 
{ 
    int n; 
    double d; 
public: 
    Test(int a, double b = 0.0): n(a), d(b) { std::cout << "Test(" << a << ", " << b << ")\n"; } 
    Test(const Test& x): n(x.n), d(x.d) { std::cout << "Test(const Test&)\n"; } 
    void foo() const { std::cout << "Test.foo(" << n << ", " << d << ")\n"; } 
}; 

int main() 
{ 
    Test test(4, 3.14); 
    Container<Test> a(construct_with(1), test); //first constructed internally, second copied 
    a.first.foo(); 
    a.second.foo(); 
} 
Các vấn đề liên quan