简体   繁体   English

C ++共享指针。 如何更改所有副本的基础对象的指针?

[英]C++ Shared pointers. How can I change the underlying object's pointer for all copies?

I have a strange situation with virtual classes and I need design help. 虚拟课我有一个奇怪的情况,我需要设计帮助。

  1. The objects do work on destruction, 对象确实可以破坏,
  2. The objects are stored in a vector, but I need to get the object or a reference to it out of the vector, change it, and have that change reflected in ALL 'copies' of the object, 对象存储在向量中,但是我需要从向量中获取对象或对它的引用,更改它,并将该更改反映在对象的所有“副本”中,
  3. I'd like the objects to be copyable. 我希望这些对象可以复制。

I have a solution, but I am looking for a better one. 我有一个解决方案,但我正在寻找一个更好的解决方案。 This is the code I've written that accomplishes what I am looking for, but it depends on pointers to pointers to pointers. 这是我编写的代码,它完成了我正在寻找的东西,但它依赖于指向指针的指针。

I feel like if I could just directly manipulate the underlying data pointer of the shared pointer, I could get away with one less layer of abstraction. 我觉得如果我可以直接操作共享指针的底层数据指针,我可以减少一层抽象。

I would like to do this with one InterfaceWrapper, not two. 我想用一个InterfaceWrapper,而不是两个。

#include <stdio.h>
#include <memory>
#include <vector>

class Interface
{
  public:
    virtual void WriteIt() = 0;
    virtual ~Interface() { }
};

class Foo : public Interface
{
  void WriteIt() { printf ("Foo\n"); }
};

class Bar : public Interface
{
  void WriteIt() { printf ("Bar\n"); }
};

// This class wraps Interface so we can call WriteIt on desctruction
// I'd like to do this in the Interface class, but you can't call virtual methods during destruction.
class InterfaceWrapper : public std::unique_ptr<Interface>
{
  public:
  InterfaceWrapper(Interface * i) : std::unique_ptr<Interface>(i) { }
  ~InterfaceWrapper() { (*this)->WriteIt(); }
};

// This class provides counted destruction to InterfaceWrapper
class InterfaceWrapper2
{
  public:
    InterfaceWrapper2 () : _ptr(new InterfaceWrapper(new Foo)) { }

    void MakeBar() { _ptr->reset(new Bar); }

  private:
    std::shared_ptr<InterfaceWrapper> _ptr;
};

int main (void)
{
  std::vector<InterfaceWrapper2> thing_vector;

  // The default implementation will, print "Foo" on destruction.
  InterfaceWrapper2 thing;

  // That destructor should only happen once for all copies of 'thing'
  thing_vector.push_back(thing);

  // MakeBar changes the virtual implementation so it prints "Bar" instead of "Foo"
  thing.MakeBar();

  // When destructors happen, the program should just print "Bar" once.

  return 0;
}

Anything is welcome, but I particularly interested in solutions which work on C++03 using boost (my example is C++11 but my 'real' code is C++03 using boost::shared_ptr). 欢迎任何事情,但我特别感兴趣的是使用boost在C ++ 03上工作的解决方案(我的例子是C ++ 11,但我的'真正的'代码是使用boost :: shared_ptr的C ++ 03)。

CLARIFICATION 澄清

I am basically looking for a better way to implement InterfaceWrapper2 in my example code. 我基本上是在寻找在我的示例代码中实现InterfaceWrapper2的更好方法。 main() is the best explanation of what I am trying to accomplish. main()是我想要完成的最好的解释。 Just remember the behavior is stuck in those virtual classes. 只记得行为被困在那些虚拟类中。

Is this what you're after? 这就是你要追求的吗?

Update 3 更新3

If you want to keep the Interface machinery, the following is a pretty succinct way to write the wrapper by just composing standard library features: 如果你想保留接口机制,以下是通过编写标准库特性来编写包装器的一种非常简洁的方法:

class InterfaceWrapper {
    using UI = std::unique_ptr<Interface>;

    std::shared_ptr<UI> _sui {new UI{new Foo}, [](UI*p){ (*p)->WriteIt(); delete p; }};

public:
    void MakeBar() { _sui->reset(new Bar); }
};

See it Live On Coliru 看到Live On Coliru

Update 2 更新2

  1. After realizing that an std::function<> is already a dynamic, single-function, mutable interface that can be bound to any stateful functor, I thought of the following simplified version: 在意识到std::function<>已经是一个可以绑定到任何有状态仿函数的动态,单函数,可变接口之后,我想到了以下简化版本:

    Live On Coliru 住在Coliru

     #include <memory> #include <iostream> #include <vector> struct dynamic_release { template <typename F> dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~dynamic_release() { _f(); } private: std::function<void()> _f; }; void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } int main(void) { using InterfaceWrapper = std::shared_ptr<dynamic_release>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once } 
  2. To enable optimizations in case you want to achieve more efficiency with state-less functors too, add a basic_dynamic_release template that allows to use different functor types (like, eg void(*)() ): 要在想要使用无状态basic_dynamic_release函数实现更高效率的情况下启用优化,请添加允许使用不同basic_dynamic_release函数类型的basic_dynamic_release模板(例如, void(*)() ):

    Live On Coliru 住在Coliru

     #include <memory> #include <iostream> namespace detail { template <typename InterfaceCallable> struct basic_dynamic_release { basic_dynamic_release() = default; template <typename F> basic_dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> basic_dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~basic_dynamic_release() { _f(); } private: InterfaceCallable _f; }; } using dynamic_release = detail::basic_dynamic_release<std::function<void()>>; #include <vector> void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } int main(void) { using InterfaceWrapper = std::shared_ptr<detail::basic_dynamic_release<void(*)(void)>>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once } 
  3. To allow for default constructed instances to have a well defined implementation of the interface, adding a factory (this is to make it very generic): 为了允许默认构造的实例具有良好定义的接口实现,添加工厂(这是为了使它非常通用):

    Live On Coliru 住在Coliru

     #include <memory> #include <iostream> namespace detail { template <typename T> struct default_construction final { T operator()() const { return {}; } }; template <typename InterfaceCallable, typename Factory = default_construction<InterfaceCallable> > struct basic_dynamic_release { basic_dynamic_release() = default; template <typename F> basic_dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> basic_dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~basic_dynamic_release() { _f(); } private: InterfaceCallable _f = Factory()(); }; using dynamic_interface = std::function<void()>; template <typename Factory = default_construction<dynamic_interface> > using dynamic_release = basic_dynamic_release<dynamic_interface, Factory>; } #include <vector> void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } struct foo_default { detail::dynamic_interface operator()() const { return do_foo; } }; int main(void) { using InterfaceWrapper = std::shared_ptr<detail::dynamic_release<foo_default> >; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = &do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once } 

Old answer 老答案

The old answer favoured static polymorphism using boost::variant , at the cost of being somewhat more complex the manage, but with greater flexibility: 旧的答案倾向于使用boost::variant静态多态性,代价是管理稍微复杂一些,但具有更大的灵活性:

I opted to replace the dynamic polymorphism with static polymorphism, which removes the extra allocation, which also takes with it the lifetime management (what used to be the unique_ptr ). 我选择用静态多态替换动态多态,这会消除额外的分配,这也会带来生命周期管理(曾经是unique_ptr )。

I think this makes resulting solution a bit simplified, and at the same time more generic (naturally provides some extension points). 我认为这使得最终解决方案有点简化,同时更通用(自然提供一些扩展点)。

Live On Coliru 住在Coliru

 #include <boost/variant.hpp> #include <memory> #include <iostream> namespace nature { // detail namespace template <typename> struct Nature; template<> struct Nature<struct FooTag> { void do_it() { std::cout << "Foo" << "\\n"; } }; template<> struct Nature<struct BarTag> { void do_it() { std::cout << "Bar" << "\\n"; } }; using FooNature = Nature<FooTag>; using BarNature = Nature<BarTag>; using AnyNature = boost::variant<FooNature, BarNature>; struct Holder { AnyNature held; ~Holder() { DoIt()(held); } private: struct DoIt : boost::static_visitor<> { void operator()(AnyNature& any) const { return boost::apply_visitor(*this, any); } template <typename N> void operator()(N& nature) const { return nature.do_it(); } }; }; } #include <vector> int main(void) { using InterfaceWrapper = std::shared_ptr<nature::Holder>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); // FooNature is default thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing->held = nature::BarNature {}; // prints nothing thing_vector.push_back(thing); } // prints "Bar" once } 

Prints 打印

 Foo Bar 

Borrowing from sehe's brilliant insight , this version gives you something that is closer to what you can do within C++.2003: 借用sehe的精彩洞察力 ,这个版本为您提供了一些与您在C ++中可以做的更接近的东西.2003:

class InterfaceWrapper {
    typedef std::unique_ptr<Interface> UI;
    std::shared_ptr<UI> p_;
public:
    InterfaceWrapper () : p_(std::make_shared<UI>(new Foo)) {}
    ~InterfaceWrapper () { if (p_.unique()) (*p_)->WriteIt(); }
    void MakeBar() { p_->reset(new Bar); }
    const UI & operator -> () { return *p_.get(); }
};

Instead of a custom deleter, I chose to add a conditional within the destructor of InterfaceWrapper . 我选择在InterfaceWrapper的析构函数中添加条件,而不是自定义删除器。 This allowed the constructor to use std::make_shared . 这允许构造函数使用std::make_shared

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C ++指针。 如何为指针结构赋值? - C++ Pointers. How to assign value to a pointer struct? C ++如何为接受对象指针数组的函数编写头文件原型。 - C++ how to write header file prototype for a function that accepts an array of object pointers. C++ 使用头指针数组的哈希表。 如何将我的 addNode() 私有方法分配给我的头指针键 position? - C++ hashtable using an array of head pointers. How do i assign my addNode() private method to my head pointer key position? C ++函数指针。 它如何以及为什么起作用? - C++ Function Pointers. How and why it works? C++ 游戏出现错误,我很确定指针有问题。 但我不能告诉 - Having an error with C++ game, I'm pretty sure it's something wrong with the pointers. but i cant tell c++ inheritance问题及指点。 尝试使用指针访问子 class 但无法找到 function - c++ inheritance problem and pointers. Trying to access child class with a pointer but the function is not being able to be found 在C ++中,传递指针仍会复制对象吗? - In C++, passing a pointer still copies the object? 无法理解C ++指针中的输出。 (使用指针中的循环) - Unable to understand the output in C++ Pointers. (Working with loops in pointers) 我想将指向const对象的指针放入容纳非const指针的容器中。 我有什么选择? - I want to put a pointer to an object that is const into a container that holds non-const pointers. What are my options? C++ 基于指针向量循环的范围。 如何通过 const 引用捕获元素? - C++ range based for loop over vector of pointers. How to capture elements by const reference?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM