是否有可能避免将传递给保存指针的函数复制传递给它的函数?

发布于 2025-01-21 07:39:01 字数 1229 浏览 1 评论 0原文

我有一个类holder的vector,该向量具有从同一抽象基础类(base)派生的不同类(例如)的实例(例如)。我将其构建为基础阶级的指针,以支持多态性。通过称为add的方法添加新实例。呼叫添加时,新类是在括号内构造的,并且未命名(此机制提供给我,无法更改)。

我不太了解该构建类的寿命是什么,以及在退出添加后如何防止其破坏。它必须构建,因此是否有一种方法可以保持其活力并持有有效的指针?还是我必须创建一个副本并让通过的临时死亡?

我使用的是C ++ 17,我的类都没有手动分配内存,仅使用STL容器和原始数据类型。我一直在寻找std :: move(),但是我找不到所需的东西。示例代码:

class Base
{
public:
  std::vector<int> vec;
  virtual void foo() = 0;
}
class Derived : public Base
{
public:
  Derived(int a, int b)
  {
    vec.push_back(a);
    vec.push_back(b);
  }
  void foo() override;
}
class Holder
{
public:
  std::vector<Base*> ptrs;
  void add(Base& arg)     // What should be the type of arg? (Does not have to be Base&)
  {
    ptrs.push_back(&arg); // How should I add a pointer to arg into ptrs so that
                          // arg stays alive after returning from this method?
  }
}
int main()
{ 
  Holder h;
  h.add(Derived(1, 2));   // this is how add() gets called, I cannot change it
  ...
}

编辑:要澄清,我可以更改add的签名和正文,以及vector ptrs使用的数据类型。我无法更改add的方式(h.add(derived(1,2));)。

I have a class Holder with a vector that holds instances of different classes (e.g. Derived) derived from the same abstract base class (Base). I constructed it as a vector of pointers to the base class to support polymorphism. New instances are added to the vector via a method called add. The new classes are constructed inside the parentheses when calling add and are not named (this mechanism is supplied to me and cannot be changed).

I do not quite understand what the lifetime of this constructed class is and how can I prevent it from being destructed right after exiting add. It has to be constructed, so is there a way to keep it alive and hold a valid pointer to it? Or will I have to create a copy and let the passed temporary die?

I am using C++17, none of my classes allocate memory manually, only STL containers and primitive data types are used. I've been looking at std::move() but I did not find what I needed. Example code:

class Base
{
public:
  std::vector<int> vec;
  virtual void foo() = 0;
}
class Derived : public Base
{
public:
  Derived(int a, int b)
  {
    vec.push_back(a);
    vec.push_back(b);
  }
  void foo() override;
}
class Holder
{
public:
  std::vector<Base*> ptrs;
  void add(Base& arg)     // What should be the type of arg? (Does not have to be Base&)
  {
    ptrs.push_back(&arg); // How should I add a pointer to arg into ptrs so that
                          // arg stays alive after returning from this method?
  }
}
int main()
{ 
  Holder h;
  h.add(Derived(1, 2));   // this is how add() gets called, I cannot change it
  ...
}

EDIT: To clarify, I can change the signature and body of add as well as the data type that vector ptrs uses. I cannot change the way add is called (h.add(Derived(1, 2));).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

天涯离梦残月幽梦 2025-01-28 07:39:01

main中,

传递的参数是在调用添加返回之后被暂时销毁的。您可以存储此值,地址,但是您绝对不能解雇它。

如果要确保对象在holder的寿命中保持有效,那么您唯一的选择是接管现有对象的所有权或制作拥有您拥有的副本。

使用std :: unique_ptr将现有对象的所有权

,可以使所有权明确。

class Holder
{
    ...
    void add(std::unique_ptr<Base>&& arg)
    {
        //preferrably make ptrs a std::vector<std::unique_ptr<Base>> and use ptrs.emplace_back(std::move(arg)) or
        ptrs.push_back(args.get());
        args.release(); // make sure the object gets deleted before or during destruction of the Holder object
    }
};

制作副本

在此处使用移动构造函数在此处无效

class Holder
{
    ...

    template<class T>
    void add(T&& arg)     // What should be the type of arg?
    {
        ptrs.push_back(new T(std::forward<T>(arg));
        // Note: make sure the object created here gets destroyed
    }
};

编辑

根据更新的答案,这是holder我将继续执行的实现:

class Holder
{
public:
    std::vector<std::unique_ptr<Base>> ptrs; // make this private?

    template<class T>
    void add(T&& arg)
    {
        // better compiler error on incorrect use
        static_assert(std::is_base_of_v<Base, T>,
                      "parameter passed must be an object of a class derived from Base");

        ptrs.reserve(ptrs.size() + 1); // if an std::bad_alloc happens, this gives you a strong exception guarantee
        
        ptrs.emplace_back(std::make_unique<T>(std::forward<T>(args)));
    }
};

注意:这仅适用于base的虚拟破坏者;如果这不是一个选项,则需要保存逻辑,以与对象并肩调用正确的破坏者。您不会阻止创建临时派生的对象,但至少可以使用隐式创建的移动构造函数的派生来防止base :: vec

In main

the parameter passed is a temporary destroyed just after the call to add returns. You may store this value, the address, but you must never dereference it.

If you want to ensure the the object remains valid for the lifetime of Holder your only option is taking over the ownership of an existing object or making a copy you've got ownership of.

Taking ownership of an existing object

For this using std::unique_ptr would be preferrable to make the ownership clear.

class Holder
{
    ...
    void add(std::unique_ptr<Base>&& arg)
    {
        //preferrably make ptrs a std::vector<std::unique_ptr<Base>> and use ptrs.emplace_back(std::move(arg)) or
        ptrs.push_back(args.get());
        args.release(); // make sure the object gets deleted before or during destruction of the Holder object
    }
};

Making a copy

Use the move constructor here to void a copy

class Holder
{
    ...

    template<class T>
    void add(T&& arg)     // What should be the type of arg?
    {
        ptrs.push_back(new T(std::forward<T>(arg));
        // Note: make sure the object created here gets destroyed
    }
};

EDIT

According to the updated answer this is the implementation of Holder I'd go with:

class Holder
{
public:
    std::vector<std::unique_ptr<Base>> ptrs; // make this private?

    template<class T>
    void add(T&& arg)
    {
        // better compiler error on incorrect use
        static_assert(std::is_base_of_v<Base, T>,
                      "parameter passed must be an object of a class derived from Base");

        ptrs.reserve(ptrs.size() + 1); // if an std::bad_alloc happens, this gives you a strong exception guarantee
        
        ptrs.emplace_back(std::make_unique<T>(std::forward<T>(args)));
    }
};

Note: This only works with a virtual destructor of Base; if this isn't an option, you'll need to save logic for invoking the correct destructor alongside the object. You do not prevent the creation of the temporary Derived object, but at least it's possible to use the implicitly created move constructor of Derived to prevent unnecessary copies of the elements of Base::vec

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文