Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 独特的ptr和转发声明:编写工厂函数的正确方法_C++_C++11_Smart Pointers_Forward Declaration - Fatal编程技术网

C++ 独特的ptr和转发声明:编写工厂函数的正确方法

C++ 独特的ptr和转发声明:编写工厂函数的正确方法,c++,c++11,smart-pointers,forward-declaration,C++,C++11,Smart Pointers,Forward Declaration,最近我学习了智能PTR,我正在尝试编写一个返回唯一PTR的工厂函数。阅读了几篇关于将创建时间与显式定义的ctor和dtor放在同一个cpp文件中的文章后,我认为我可以这样做: // factory.hpp struct Foo; std::unique_ptr<Foo> create(); 但是我得到了不完整的类型错误。然后经过几个小时的网络搜索和实验, 我意识到我甚至不能这样做: 这里有一个经典的独特的\u ptr Pimpl习惯用法 // A.hpp struct B;

最近我学习了智能PTR,我正在尝试编写一个返回唯一PTR的工厂函数。阅读了几篇关于将创建时间与显式定义的ctor和dtor放在同一个cpp文件中的文章后,我认为我可以这样做:

// factory.hpp

struct Foo;

std::unique_ptr<Foo> create();
但是我得到了不完整的类型错误。然后经过几个小时的网络搜索和实验, 我意识到我甚至不能这样做:

这里有一个经典的独特的\u ptr Pimpl习惯用法

// A.hpp

struct B;

struct A {
    A();
    ~A();
    unique_ptr<B> b;
};
#包括“A.hpp”
int main(){
A;//这很好,因为我们正确地执行了Pimpl。
//现在,我不能这么做。

auto b=std::move(a.b);//当编译器实例化
std::unique_ptr
的析构函数时,编译器必须找到
Foo::~Foo()
并调用它。这意味着
Foo
必须是
std::unique_ptr
被销毁点的完整类型

这个代码很好:

struct Foo;

std::unique_ptr<Foo> create();
您似乎正在使用
std::unique\u ptr
正确地实现pimpl。您必须在
B
完成的位置(在cpp文件中)定义
A::~A()
。您必须定义
A::A()
位于同一位置,因为如果要分配内存并调用其构造函数,则必须完成
B

所以这很好:

// a.hpp

struct A {
  A();
  ~A();

private:
  struct B;
  std::unique_ptr<B> b;
};

// a.cpp

struct A::B {
  // ...  
};

A::A()
  : b{std::make_unique<B>()} {}

A::~A() = default;
这到底是怎么回事

  • 我们正在构建一个
    std::unique_ptr
    来初始化
    b
  • b
    是一个局部变量,这意味着它的析构函数将在作用域的末尾被调用
  • 当实例化
    std::unique_ptr
    的析构函数时,
    B
    必须是完整类型
  • B
    是不完整的类型,因此我们无法销毁
    B
  • 好的,如果
    B
    是不完整的类型,那么您不能传递
    std::unique_ptr
    。这个限制是有意义的。pimpl表示“指向实现的指针”。外部代码访问
    A
    的实现没有意义,因此
    A::b
    应该是私有的。如果必须访问
    A::b
    ,则这不是pimpl,这是其他内容

    如果您确实必须访问
    A::b
    ,同时隐藏
    b
    的定义,那么有一些解决方法

    std::shared_ptr
    。这会以多态方式删除对象,这样在实例化
    std::shared_ptr
    的析构函数时,
    B
    不需要是完整类型。它没有
    std::unique_ptr
    那么快,我个人更喜欢避免
    std::shared_ptr
    ,除非绝对必要

    std::unique_ptr
    。类似于
    std::shared_ptr
    删除对象的方式。函数指针传递给负责删除的构造。这会产生不必要地携带函数指针的开销

    std::unique_ptr
    。这是最快的解决方案。但是,如果您有多个pimpl(但不是真正的pimpl)类,这可能会有点烦人,因为您无法定义模板。您可以这样做:

    // a.hpp
    
    struct DeleteB {
      void operator()(B *) const noexcept;
    };
    
    // a.cpp
    
    void DeleteB::operator()(B *b) const noexcept {
      delete b;
    }
    

    定义自定义删除程序可能是最好的选择,但如果我是你,我会找到一种方法来避免从类外访问实现细节。

    你在factory.hpp中声明了
    create
    ,然后定义了
    create
    (作为非内联函数)在foo.hpp中。发生了什么?@Kerndog73抱歉,这是一个打字错误。经过编辑。应该是一个cpp文件。谢谢你的回答。我同意你关于避免
    shared\u ptr
    和不必要的函数指针的意见。所以总结一下,如果我不想包含类原型,我必须提供一个自定义的删除器。最好的方法是使用functor喜欢你的
    DeleteB
    。还有一个后续问题,函子的情况不会产生任何额外的空间,对吗?@TerryTsao是的。就是这样。另一个后续问题,对于函子的情况,与默认的DeleteB情况相比,它不会产生任何额外的空间或时间,我想得对吗?@TerryTsao是的。
    DeleteB
    require与
    std::default\u delete
    相比,没有额外的空间或时间。事实上,
    std::default\u delete
    的定义非常相似。
    #include "A.hpp"
    
    
    int main() {
        A a;   // this is fine since we are doing the Pimpl correctly.
    
        // Now, I can't do this.
        auto b = std::move(a.b);   // <--- Can't do this.
    
        return 0;
    }
    
    struct Foo;
    
    std::unique_ptr<Foo> create();
    
    #include "foo.hpp"
    
    std::unique_ptr<Foo> create();
    
    // a.hpp
    
    struct A {
      A();
      ~A();
    
    private:
      struct B;
      std::unique_ptr<B> b;
    };
    
    // a.cpp
    
    struct A::B {
      // ...  
    };
    
    A::A()
      : b{std::make_unique<B>()} {}
    
    A::~A() = default;
    
    int main() {
      A a;
      auto b = std::move(a.b);
    }
    
    // a.hpp
    
    struct DeleteB {
      void operator()(B *) const noexcept;
    };
    
    // a.cpp
    
    void DeleteB::operator()(B *b) const noexcept {
      delete b;
    }