【问题标题】:How to move an object instead of copying it in c++?如何移动对象而不是在 C++ 中复制它?
【发布时间】:2021-07-09 07:29:45
【问题描述】:

有没有办法可以移动在 main 中创建的对象 GuitarSpec 而不是复制它?

所以这里是下面的例子:- 有一个包含吉他列表的 Inventory 类,要添加吉他,有一个名为 addGuitar 的函数,它接受字符串、双精度和 GuitarSpec 对象作为参数。

库存

class Inventory {
 private:
  list<Guitar> inventory;
public:
  void addGuitar(const string &, double, const GuitarSpec &spec);

添加吉他功能

void Inventory::addGuitar(const string &serialNumber, double price,
                          const GuitarSpec &spec) {
  inventory.emplace_back(serialNumber, price, spec);
}

吉他构造器

Guitar::Guitar(const string& serialNumber, double price, const GuitarSpec &spec)
    : serialNumber{serialNumber}, price{price}, spec(spec) {
  cout << "Guitar Constructor" << endl;
}

主要功能:-

 Inventory inventory;
inventory.addGuitar(
      "1001", 200,
      GuitarSpec(toString(FEDER), "starocaster", toString(ELECTRIC),
                 toString(Wood::SIKTA), toString(Wood::SIKTA)));

有没有办法移动那个 GuitarSpec 对象而不是复制它,或者任何其他更好的解决方案?

【问题讨论】:

  • 除非您有非常具体的需求和要求,否则默认容器类型应为std::vector。你选择std::list的原因是什么(我猜是)?
  • GuitarSpec 可以移动吗?您是否尝试过实施动作?你遇到了什么问题?请出示minimal reproducible example
  • @Someprogrammerdude 我实际上正在阅读 Head First OOA 和 Design,示例使用列表而不是矢量,所以我选择了它。
  • @Mohammad Hussein Spec 参数必须是可变的或通过 GuitarSpec&amp;&amp; 右值引用传递,而不是通过 const 引用传递,那么只要规范设计正确,std::move 就足够了。见stackoverflow.com/questions/28595117/…你也可以将Guitar构造函数作为模板并使用完美转发。

标签: c++ oop constructor copy-constructor move-constructor


【解决方案1】:

当您考虑只移动一个参数时,您可能会避免声明函数重载,其中一个将从临时移动。或者您的设计中使用了 oly 临时对象,那么您为什么不遵循“就位”策略并在现场创建新对象?

但如果移动了两个或更多参数,则所需的重载数量将是四个、八个等等。这不好,在这种情况下,完美前锋可能更有用。 C++11风格转发单个参数(Spec类型)示例:

#include <iostream>
#include <utility>

struct Data {
    Data(const Data&) { std::cout << "Data copied\n"; }
    Data()  { std::cout << "Data created\n"; }
};

struct Spec {
    Data *ptr;
    
    Spec() : ptr(new Data()) {};
    
    Spec(const Spec& other) : ptr(new Data{*other.ptr}) {};
    
    Spec(Spec && other) : ptr(other.ptr) { 
          other.ptr = nullptr;
          std::cout << "Data moved\n";                      
    }
    
    Spec& operator=(const Spec& other) { ptr = new Data{*other.ptr};
                                        std::cout << "Data copied\n";
                                        return *this; }
    Spec& operator=(Spec&& other) { ptr = other.ptr; other.ptr = nullptr; 
                                    std::cout << "Data moved\n";  return *this; 
                                  }
    
    ~Spec() { delete ptr; }
};


struct foo {
    Spec d;
    
    template < typename T, std::enable_if_t<std::is_convertible<T, Spec>::value> * = nullptr>
    foo(T&& v) : d(std::forward<T>(v)) { }
    
    template <typename T>
    auto set_spec(T&& v) -> decltype(v = std::forward<Spec>(v), void())
    { d = std::forward<T>(v); }
};

int main()
{
    std::cout << "Move\n";
    foo a {Spec()};
    a.set_spec(Spec());
   
    
    std::cout << "Copy\n";
    Spec s;
    foo b {s};
    a.set_spec(s);
}

你必须修改整个责任链才能使用它,从重载 Inventory 的方法开始:

void addGuitar(const string &serialNumber, double price, GuitarSpec&& spec) {
     // move, do we want move string?
     inventory.emplace_back(serialNumber, price, std::move(spec));
}

或使用完美转发,此模板可以在适当的时候复制或移动(没有 SFINAE 的示例):

template <class SN, class SP>
void addGuitar(SN&& serialNumber, double price, SP&& spec) 
{
  
      inventory.emplace_back(std::forward<std::string>(serialNumber), 
                             price, std::forward<GuitarSpec>(spec));
}

从技术上讲 addGuitar 可能只是,如果我们不想打扰 SFINAE 限制接口,假设 我们总是会正确使用它并且不会发生任何错误(墨菲,把你的手down) 如果它不是公共接口。在具有长生命周期和多个开发人员的大型项目中的错误假设。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 2021-09-17
    • 2016-05-17
    • 1970-01-01
    • 2017-02-15
    • 1970-01-01
    相关资源
    最近更新 更多