很长一段时间后,我又回到了 C++,继承了一个代码库,其中一部分具有以下代码中的结构。我有两个对象,Wrapped和,Wrapper并且正如所料,Wrapper包装了Wrapped。有两种工厂方法make_Wrapper和,make_Wrapped它们返回指向相应对象的指针。

在下面的代码中,我发现Case1使用shared_ptr不起作用,而Case2使用普通 C++ 指针却起作用。“不起作用”是指Case1 中nz_=10的值wrapped被垃圾替换,而 Case2 中的值等于 10。我可以通过 打印 中的值来检查wrapper.nz_一点mainoutside_wrapper->print()

我认为这是因为该函数退出后,shared_ptr 指向的对象会被删除make_Wrapper。我说得对吗?

shared_ptr通过从make_Wrapper到 构造函数传递对象的正确方法是什么Wrapper

#include <iostream>
#include <memory>
#include <functional>

// this is struct which is to be wrapped
struct Wrapped {
  int nz_;
  Wrapped(int nz):nz_(nz) {}
  inline virtual void destroy() { delete this; };
};

// this is the wrapper
struct Wrapper {
  Wrapper(Wrapped& wrapped):wrapped_(wrapped){}
  Wrapped& wrapped_;
  
  inline virtual void destroy() { delete this; };
  
  void print() {
    std::cout << "from wrapper: wrapped_.nz_=" << wrapped_.nz_<<std::endl;
  }
};

// factory function which returns pointer to wrapped object
Wrapped* make_Wrapped(int nz) {
  return new Wrapped(nz);
}


// factory function which returns pointer to wrapper object
Wrapper* make_Wrapper(int nz) {

  // Case 1
  // following line does not work - I think the object pointed to by the shared_ptr is deleted after this function exits
  std::shared_ptr<Wrapped> wrapped(make_Wrapped(nz), std::mem_fun(&Wrapped::destroy));

  // Case 2
  // following line works
  // Wrapped *wrapped = new Wrapped(nz);
  
  std::cout<<" from make_Wrapper "<<(*wrapped).nz_<<std::endl;
  return new Wrapper(*wrapped);
}

int main() {
    int nz=10;
    char tempbuffer[80];
    
    std::shared_ptr<Wrapper> outside_wrapper(make_Wrapper(nz), std::mem_fun(&Wrapper::destroy));
    outside_wrapper->print();

    std::cout << "Enter something to continue ..." << std::endl;
    std::cin >> tempbuffer;
    return 0;
}

13

  • “不起作用”和“按预期工作”是什么意思?你遇到错误了吗?如果有,请发布它们。如果没有遇到任何错误,请解释预期输出是什么以及它与当前输出有何不同。


    – 


  • 在 Case1 中,wrapped 中的值nz_=10被 junk 替换,而在 Case2 中,该值等于 10


    – 

  • 也将此添加到您的问题中。


    – 

  • 1
    使用unique_ptr@wohlstad 在其答案中展示的方式。第一个选择应该是unique_ptr(当然,除了需要共享所有权的情况)如该答案中所示。


    – 


  • 1
    根据我的经验,如果destroy方法执行的是 a,delete this;则是一种 C++ 反模式,它源自从另一种语言移植 C++ 代码的幼稚做法,而另一种语言使用惯用的 alloc-init-destroy-free 模式。 init 应该在构造函数中,destroy 应该在析构函数中。 不delete this;。 (还有另一种 C++ 反模式,即先进行两阶段构造,然后再进行 init。)


    – 


最佳答案
2

您可以使用智能指针将Wrapped对象保存在 中Wrapper。使用智能指针时,无需原始指针和手动new/ delete。也不需要(至少默认情况下)显式destroy()方法。

在下面的例子中我使用了,它不支持复制,因此在需要时用 d 代替。
std::unique_ptr在足够时应该是默认的智能指针。

如果您想要共享所有权,则可以使用std::make_shared(然后您只需删除std::moves 即可)。

#include <iostream>
#include <memory>

// this is struct which is to be wrapped
struct Wrapped {
    int nz_;
    Wrapped(int nz) :nz_(nz) {}
};

// this is the wrapper
struct Wrapper {
    Wrapper(std::unique_ptr<Wrapped> wrapped) :wrapped_(std::move(wrapped)) {}
    std::unique_ptr<Wrapped> wrapped_;

    void print() {
        std::cout << "from wrapper: wrapped_.nz_=" << wrapped_->nz_ << std::endl;
    }
};

// factory function which returns pointer to wrapped object
std::unique_ptr<Wrapped> make_Wrapped(int nz) {
    return std::make_unique<Wrapped>(nz);
}

// factory function which returns pointer to wrapper object
std::unique_ptr<Wrapper> make_Wrapper(int nz) {
    auto wrapped = make_Wrapped(nz);
    std::cout << " from make_Wrapper " << wrapped->nz_ << std::endl;
    auto wrapper = std::make_unique<Wrapper>(std::move(wrapped));
    return wrapper;
}

int main() {
    int nz = 10;
    auto outside_wrapper = make_Wrapper(nz);
    outside_wrapper->print();
}

输出:

 from make_Wrapper 10
from wrapper: wrapped_.nz_=10

既然您明确询问了std::shared_ptr

这里也有一个演示:

一旦一个对象被 包裹,shared_ptr它将在最后一个指向它的对象被销毁时也被销毁shared_ptr,这发生在 的末尾make_Wrapper

解决方案:

让 Wrapper 存储一个shared_ptr而不是仅仅一个引用,现在Wrapped只要还Wrapper活着就会保持活跃。

struct Wrapper {
    Wrapper(std::shared_ptr<Wrapped> wrapped) :wrapped_(std::move(wrapped)) {}
    std::shared_ptr<Wrapped> wrapped_;

    inline virtual void destroy() { delete this; };

    void print() {
        std::cout << "from wrapper: wrapped_.nz_=" << wrapped_->nz_ << std::endl;
    }
};

// factory function which returns pointer to wrapper object
Wrapper* make_Wrapper(int nz) {

    std::shared_ptr<Wrapped> wrapped(make_Wrapped(nz), std::mem_fun(&Wrapped::destroy));

    std::cout << " from make_Wrapper " << (*wrapped).nz_ << std::endl;
    return new Wrapper(std::move(wrapped));
}