gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:39:11 24 4
gpt4 key购买 nike

我在使用虚拟类时遇到了一个奇怪的情况,我需要设计方面的帮助。

  1. 对象确实会破坏,
  2. 对象存储在 vector 中,但我需要从 vector 中获取对象或对它的引用,更改它,并将该更改反射(reflect)在对象的所有“拷贝”中,
  3. 我希望对象是可复制的。

我有解决方案,但我正在寻找更好的解决方案。这是我编写的代码,它实现了我正在寻找的东西,但它依赖于指向指针的指针。

我觉得如果我可以直接操作共享指针的底层数据指针,我就可以少一层抽象。

我想用一个 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;
}

任何东西都是受欢迎的,但我对使用 boost 在 C++03 上工作的解决方案特别感兴趣(我的示例是 C++11,但我的“真实”代码是使用 boost::shared_ptr 的 C++03)。

澄清

我基本上是在寻找一种更好的方法来在我的示例代码中实现 InterfaceWrapper2。 main() 是对我想要完成的事情的最好解释。请记住,行为停留在那些虚拟类中。

最佳答案

这就是你想要的吗?

更新 3

如果你想保留接口(interface)机制,下面是一个非常简洁的方法来编写包装器,只需组合标准库功能:

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); }
};

查看 Live On Coliru

更新 2

  1. 在意识到 std::function<>已经是一个动态的、单函数的、可变的接口(interface),可以绑定(bind)到任何有状态的仿函数,我想到了以下简化版本:

    Live On 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. 如果您也想通过无状态仿函数实现更高效率,要启用优化,请添加 basic_dynamic_release允许使用不同仿函数类型的模板(例如 void(*)() ):

    Live On 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. 为了让默认构造的实例具有定义良好的接口(interface)实现,添加一个工厂(这是为了使其非常通用):

    Live On 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
    }

旧答案

旧答案倾向于使用 boost::variant 的静态多态性,以管理更复杂为代价,但具有更大的灵 active :

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).

I think this makes resulting solution a bit simplified, and at the same time more generic (naturally provides some extension points).

Live On 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

关于C++ 共享指针。如何更改所有拷贝的基础对象指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27260057/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com