【问题标题】:How to pass a reference of manager to the handler object?如何将管理器的引用传递给处理程序对象?
【发布时间】:2013-03-29 17:08:40
【问题描述】:

我想做的是写一个小的Manager/Handler类。管理者分发和管理句柄。例如,这样的句柄可以是一个简单的文件句柄。

如果消费者想要获取已经存在的句柄,管理器只需返回一个 shared_ptr。如果句柄不存在,则管理器创建一个新句柄,然后返回 shared_ptr。

在 Manager 内部,那些 shared_ptr 存储在一个简单的 STL-Map 中。 如果分配的最后一个 shared_ptr 被删除,我希望我的经理删除相关的地图元素,以便处理程序对象自动被破坏。

这听起来有点像垃圾收集(例如工作线程,它检查指针的使用计数),但我相信它可以做得更优雅。

如何将管理器实例的引用传递给处理程序对象? (例如,像传递一个 unique_ptr(this) 给新处理程序的构造函数)

#include <memory>
#include <iostream>
#include <map>

using namespace std;

/*
 * Simple handler class, that actually does nothing.
 * This could be e.g. a Filehandler class or sth. like that
 */
class Handler {
private:
    int i;
public:
    Handler(int i) :i(i) {}
    ~Handler() {}
    // Say who you are.
    void print(void) { cout << "I am handler # " << i << endl; }
};

/*
 * This is the "manager" class, that manages all handles. A handle is identified
 * by an integer value. If a handle already exists, the Manager returns a shared_ptr,
 * if it does not exist, the manager creates a new handle.
 */
class Manager {
private:
    map<int, shared_ptr<Handler> > handles;
public:
    Manager() {}
    ~Manager() {}

    shared_ptr<Handler> get_handler(int identifier) {
        shared_ptr<Handler> retval;
        auto it = handles.find(identifier);

        if(it != handles.end() ) {
            retval = it->second;
        } else {
            retval = shared_ptr<Handler>(new Handler(identifier));
            handles.insert( pair<int, shared_ptr<Handler>>(identifier, retval) );
        }

        return retval;
    }
};

int main(int argc, char** argv) {
    Manager m;

    // Handler 13 doesn't exist, so it gets allocated
    auto h = m.get_handler(13);
    // Manager knows about handler 13, so it returns the already existing shared_ptr
    auto i = m.get_handler(13);

    h.reset(); // Well... Let's assume we don't need h any more...
    // do some stuff...
    i->print();
    // ...
    i.reset(); // We also loose i. This is exactly the point where i want the manager to forget about the handle 13

    return 0;
}

【问题讨论】:

    标签: c++ c++11 memory-management


    【解决方案1】:

    您可能希望在管理器中保存非拥有指针以跟踪现有句柄,并使用自定义删除器放弃拥有 shared_ptr。当对象最终被销毁时,自定义删除器将确保删除管理器中相应的观察指针。

    我将这种模式称为跟踪工厂,它是这样工作的。给定一个 object 类(在你的情况下是 Handler):

    class object
    {
    
    public:
    
        size_t get_id() const
        {
            return _id;
        }
    
    private:
    
        friend class tracking_factory;
    
        object(size_t id) : _id(id) { }
    
        size_t _id = static_cast<size_t>(-1);
    
    };
    

    我定义了一个类,它创建object 的实例并将非拥有引用(weak_ptrs)存储到它们。这个类是唯一可以创建object 实例的类——这就是为什么object 的构造函数是私有的,并且tracking_factory 被声明为friend 以便能够访问它:

    class tracking_factory
    {
    
    public:
    
        std::shared_ptr<object> get_object(size_t id, 
                                           bool createIfNotFound = true)
        {
            auto i = std::find_if(
                begin(_objects),
                end(_objects),
                [id] (std::pair<size_t const, std::weak_ptr<object>> const& p) 
                -> bool
            {
                return (p.first == id);
            });
    
            if (i != end(_objects))
            {
                return i->second.lock();
            }
            else if (createIfNotFound)
            {
                return make_object(id);
            }
            else
            {
                return std::shared_ptr<object>();
            }
        }
    
        size_t count_instances() const
        {
            return _objects.size();
        }
    
    private:
    
        std::shared_ptr<object> make_object(size_t id)
        {
            std::shared_ptr<object> sp(
                new object(id),
                [this, id] (object* p)
            {
                _objects.erase(id);
                delete p;
            });
    
            _objects[id] = sp;
    
            return sp;
        }
    
        std::map<size_t, std::weak_ptr<object>> _objects;
    
    };
    

    然后,程序的其余部分将通过object_factory 获取shared_ptrs 到objects:如果已经创建了具有所需特征的对象(此处为id 成员),则为shared_ptr它将在没有实例化新对象的情况下返回。下面是一些测试功能的代码:

    #include <iostream>
    
    int main()
    {
        tracking_factory f;
    
        auto print_object_count = [&f] ()
        {
            std::cout << "Number of objects: " << f.count_instances() << std::endl;
        };
    
        print_object_count();
    
        auto p1 = f.get_object(42);
    
        print_object_count();
    
        {
            auto p2 = f.get_object(42);
    
            print_object_count();
    
            p1 = f.get_object(0);
    
            print_object_count();
        }
    
        print_object_count();
    
        p1.reset();
    
        print_object_count();
    }
    

    最后,这是live example

    【讨论】:

    • 这正是我想要的!非常感谢!
    • @rralf:不客气 :) 如果这解决了您的问题,请考虑将答案标记为已接受(或您可能喜欢的任何其他答案)。
    • 嗯,valgrind 告诉我,您的示例在某处泄漏,但我没有离开,它恰好在哪里泄漏...
    • 我认为您的自定义删除器没有辜负它的名字。
    • @rralf:我发现了问题(很尴尬,我忘了在删除器中调用delete)并且还修改了原始版本以使用地图而不是矢量 - 它大大简化了事情少量。您能否检查一下这是否适合您?
    【解决方案2】:

    在地图中存储std::weak_ptr对象;它们不保留所有权,因此当最后一个 std::shared_ptr 对象消失时,资源将被销毁。但他们确实会跟踪是否有任何剩余的std::shared_ptr 对象指向原始对象,因此将它们放在地图中可以让您稍后检查那里是否还有资源。

    【讨论】:

    • 非常感谢!但是,我会在我的地图中“死”过期的弱指针。这不是致命的,因为我可以识别它们。有没有一种聪明的方法可以在 Handler 类中存储 Manager 类的引用?然后我可以在每次处理程序被销毁时强制调用管理器内部的收集垃圾函数。
    • @rralf:你不需要垃圾收集。如果您想避免保留过期的弱指针,您可以使用自定义删除器在销毁时删除它们。我已经在我的回答中描述了这一点。
    猜你喜欢
    • 1970-01-01
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多