2016-12-08 16 views
6

Trong một ứng dụng khá lớn, tôi muốn theo dõi một số số liệu thống kê về các đối tượng của một lớp nhất định. Để không làm giảm hiệu suất, tôi muốn các số liệu thống kê được cập nhật trong một cấu hình kéo. Do đó, tôi cần phải có một tham chiếu đến từng đối tượng sống ở một số vị trí. Có một ngữ cách để:Theo dõi các đối tượng (xếp chồng được phân bổ)

  1. Tạo, tìm kiếm, lặp tài liệu tham khảo như
  2. Manage nó tự động (tức là loại bỏ các tài liệu tham khảo khi tiêu hủy)

Tôi đang nghĩ về một bộ của con trỏ thông minh ở đây, nhưng việc quản lý bộ nhớ sẽ bị đảo ngược một chút: Thay vì phá hủy đối tượng khi con trỏ thông minh bị phá hủy, tôi muốn con trỏ thông minh bị xóa, khi đối tượng bị hủy. Lý tưởng nhất, tôi không muốn phát minh lại bánh xe.

Tôi có thể sống với một sự chậm trễ trong việc loại bỏ các con trỏ, tôi chỉ cần một cách để làm mất hiệu lực chúng một cách nhanh chóng.

chỉnh sửa: Vì lúa yêu cầu: Lý do thu thập dựa trên kéo là lấy thông tin có thể tương đối tốn kém. Đẩy rõ ràng là một giải pháp sạch nhưng được coi là quá đắt.

+0

Bạn cần số liệu thống kê nào? Nếu nó chỉ theo dõi số lượng cá thể sống, hãy xem xét lưu trữ một số nguyên nguyên duy nhất trên mỗi lớp và có thể tạo một trình bao RAII đơn giản để đếm (nghĩa là bạn chỉ cần đặt nó trong định nghĩa lớp và công việc được thực hiện). – paddy

+2

Nếu sử dụng, [đây là một ví dụ đơn giản] (http://coliru.stacked-crooked.com/a/5a25d7a6a2ec18a0) về những gì tôi đang nói đến. Nó không cho phép bạn lặp lại hoặc tìm kiếm các đối tượng của bạn, nhưng nó không rõ ràng tại sao bạn muốn làm điều đó. Truy cập một ngăn xếp theo cách này có lẽ không phải là một ý tưởng tốt. Ví dụ này chỉ cung cấp số lượng đối tượng với chi phí rất thấp và an toàn chỉ. – paddy

+0

Điều đó về cơ bản sẽ là bản cập nhật đẩy, nhưng được coi là quá đắt. – choeger

Trả lời

2

Không có tính năng đặc biệt của ngôn ngữ cho phép bạn thực hiện việc này. Đôi khi việc theo dõi đối tượng được xử lý bằng cách lăn bộ cấp phát bộ nhớ của riêng bạn, nhưng điều này không hoạt động dễ dàng trên ngăn xếp.

Nhưng nếu bạn đang sử dụng chỉ ngăn xếp, nó thực sự làm cho vấn đề của bạn dễ dàng hơn, giả sử rằng các đối tượng đang được theo dõi là trên một sợi đơn. C++ tạo ra sự bảo đảm đặc biệt về thứ tự xây dựng và phá hủy trên stack. Đó là, thứ tự hủy diệt chính xác là ngược lại trật tự xây dựng.

Và vì vậy, bạn có thể tận dụng điều này để lưu trữ một con trỏ trong mỗi đối tượng, cộng với một con trỏ tĩnh để theo dõi con trỏ gần nhất. Bây giờ bạn có một đối tượng ngăn xếp đại diện như một danh sách liên kết.

template <typename T> 
class Trackable 
{ 
public: 
    Trackable() 
    : previous(current()) 
    { 
     current() = this; 
    } 

    ~Trackable() 
    { 
     current() = previous; 
    } 

    // External interface 
    static const T *head() const { return dynamic_cast<const T*>(current()); } 
    const T *next() const { return dynamic_cast<const T*>(previous); } 

private: 
    static Trackable * & current() 
    { 
     static Trackable *ptr = nullptr; 
     return ptr; 
    } 

    Trackable *previous; 
} 

Ví dụ:

struct Foo : Trackable<Foo> {}; 
struct Bar : Trackable<Bar> {}; 

// ::: 

// Walk linked list of Foo objects currently on stack. 
for(Foo *foo = Foo::head(); foo; foo = foo->next()) 
{ 
    // Do kung foo 
} 

Bây giờ, phải thừa nhận đây là một giải pháp rất đơn giản. Trong một ứng dụng lớn, bạn có thể có nhiều ngăn xếp bằng cách sử dụng các đối tượng của bạn. Bạn có thể xử lý các ngăn xếp trên nhiều luồng bằng cách thực hiện current() sử dụng ngữ nghĩa thread_local. Mặc dù bạn cần một số phép thuật để thực hiện công việc này, vì head() sẽ cần phải trỏ vào sổ đăng ký các chủ đề và điều đó sẽ yêu cầu đồng bộ hóa.

Bạn chắc chắn không muốn đồng bộ hóa tất cả các ngăn xếp thành một danh sách duy nhất, vì điều đó sẽ giết khả năng mở rộng hiệu suất của chương trình của bạn.

Đối với yêu cầu kéo của bạn, tôi cho rằng đó là một chuỗi riêng biệt muốn đi qua danh sách. Bạn sẽ cần một cách để đồng bộ hóa như vậy mà tất cả các đối tượng xây dựng mới hoặc phá hủy bị chặn bên trong Trackable<T> trong khi danh sách đang được lặp lại. Hoặc tương tự.

Nhưng ít nhất bạn có thể lấy ý tưởng cơ bản này và mở rộng nó theo nhu cầu của bạn.

Hãy nhớ rằng, bạn không thể sử dụng cách tiếp cận danh sách đơn giản này nếu bạn phân bổ động các đối tượng của mình. Cho rằng bạn sẽ cần một danh sách hai chiều.

2

Cách tiếp cận đơn giản nhất là phải có mã bên trong từng đối tượng để nó tự đăng ký ngay lập tức và tự xóa khi hủy. Mã này có thể dễ dàng được tiêm sử dụng một CRTP:

template <class T> 
struct AutoRef { 

    static auto &all() { 
     static std::set<T*> theSet; 
     return theSet; 
    } 

private: 
    friend T; 

    AutoRef() { all().insert(static_cast<T*>(this)); } 
    ~AutoRef() { all().erase(static_cast<T*>(this)); } 
}; 

Bây giờ một lớp Foo có thể kế thừa từ AutoRef<Foo> có trường hợp nó tham chiếu bên trong AutoRef<Foo>::all().

See it live on Coliru

+0

Đơn giản hơn nhiều so với giải pháp của tôi =) Mặc dù điều này có độ phức tạp logarit. Tôi coi trọng họ khi họ phàn nàn rằng có một tăng và giảm một nguyên tử cho mỗi đối tượng là "quá đắt". – paddy

+0

Ngoài ra, một vấn đề cả hai giải pháp của chúng tôi là bạn không thể đảm bảo một đối tượng vẫn còn trong sổ đăng ký hiện không bị hủy và do đó không hợp lệ, ngay cả khi bạn đồng bộ hóa quyền truy cập trên các chuỗi. Nó không khó để sửa đổi hành vi đó, nhưng nó là một chi tiết đặc biệt đáng xem xét. – paddy

+0

@paddy OP không muốn háo hức đẩy số liệu thống kê vì chúng chậm phát sinh và bị bỏ qua trong hầu hết các trường hợp. Không đề cập đến đếm hoặc đa luồng. Các container có thể dễ dàng được hoán đổi cho người khác tùy thuộc vào các mẫu sử dụng (chèn/loại bỏ tỷ lệ, số truy vấn, vv). – Quentin

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