标签: pimpl-idiom

使用 pimpl 习惯用法时 C++ 中的构造函数和析构函数

我来自 Java,它有一种不同的方式来处理私有内容,并且必须隐藏类实现,并且它还有一个垃圾收集器,这意味着不需要析构函数。

我学习了如何在 C++ 中实现类的基础知识,但在使用 pimpl 习惯用法时,我需要更好地理解如何实现类,特别是构造函数和析构函数。

.hpp 文件:

class MyClass{
public:
  MyClass();
  MyClass(std::vector<int>& arr);
  ~MyClass();

 
private:
  struct Impl;
  Impl* pimpl;
};
Run Code Online (Sandbox Code Playgroud)

.cpp 文件:

#include "MyClass.hpp"
using namespace std;


struct MyClass::Impl{
  vector<int> arr;
  int var;
};
Run Code Online (Sandbox Code Playgroud)

我使用 pimpl 惯用法编写了我需要处理的类的示例代码(这意味着我无法更改 pimpl 惯用法的使用方式),我正在寻找这些问题的答案:

  • 如何实现构造函数来创建 MyClass 的空实例?
MyClass::MyClass(){}
Run Code Online (Sandbox Code Playgroud)
  • 如何实现构造函数来使用这些参数创建 MyClass 实例?
MyClass::MyClass(vector<int>& arr,int var){}
Run Code Online (Sandbox Code Playgroud)
  • 我如何实现析构函数?
MyClass::~MyClass(){}
Run Code Online (Sandbox Code Playgroud)

编辑:

我会怎么做:

MyClass::MyClass(): pimpl(new Impl) {}

MyClass::MyClass(vector<int>& arr,int var): pimpl(new Impl) {
  pimpl->arr=arr;
  pimpl->var=var;
}

MyClass::~MyClass(){
  delete pimpl;
}

Run Code Online (Sandbox Code Playgroud)

c++ pimpl-idiom

2
推荐指数
1
解决办法
631
查看次数

pimpl、std::unique_ptr 和 constexpr 构造函数

我正在审查一个非编译代码,其中我发现了与此类似的设计:

巴赫

#include <memory>

class A;

class B {
   private:
    int val;
    // pImpl idiom
    std::unique_ptr<A> pImpl;
    constexpr B(int x): val(x){};
    virtual ~B();
};
Run Code Online (Sandbox Code Playgroud)

析构函数是在 中定义的B.cpp,但构造函数是constexpr它意味着它是在 中定义的B.h。但是编译失败,因为编译器需要有一个 的构造函数A,此时它是一个不完整的类型。但我认为这constexpr是一个设计错误,因为我看不到如何B在编译时使用实现来构造 a 。

因此,constexpr在这种情况下是错误的,还是有办法B在编译时构造 a (我不认为std::unique_ptr可以在编译时构造,除了 from nullptr)?

注意我试图将构造函数定义推入内部,B.cpp但是链接器然后(从逻辑上我认为)触发了构造函数上的未定义引用...
注意编译到目前为止仅在 msvc 上进行了测试注意
我读了一堆关于 pimpl 的帖子并且unique_ptr(它们是很多)但我可能错过了一个足够的问题,而且问题很可能是重复的......

c++ pimpl-idiom unique-ptr constexpr

2
推荐指数
1
解决办法
112
查看次数

宇宙飞船操作员与 pimpl 成语

我有一个使用 pimpl 习惯用法的类。

class MyImpl;

class MyClass
{
public:

private:
    MyImpl* _impl;
};
Run Code Online (Sandbox Code Playgroud)

现在我想向此类添加宇宙飞船操作员支持。理想情况下我会写这样的东西:

auto operator<=>(const MyClass& rhs) const
{
    return *_impl <=> *rhs._impl;
}
Run Code Online (Sandbox Code Playgroud)

但头文件中不知道其实现。因此,我必须在源文件中编写实现。但这阻止了将 用作auto返回类型。我必须明确指出它是std::strong_orderingstd::weak_ordering还是std::partial_ordering。但这在很大程度上取决于 的细节MyImpl。如果MyImpl仅包含ints,则返回类型将为std::strong_ordering,但如果也包含floats,则返回类型将为std::partial_ordering

我希望类声明MyClass不要改变太多。有什么方法可以保持其 api 稳定,同时仍然允许更改MyImpl. 我想总是回来并不理想std::partial_ordering

c++ pimpl-idiom spaceship-operator c++20

2
推荐指数
1
解决办法
109
查看次数

在pimpl类中初始化默认值的最佳位置?

我对PImpl进行了大量的使用,我发现自己正在讨论的是准确初始化Pimpl结构的成员.选项是为Private结构创建构造函数并在那里初始化它们,或者在主类的构造函数中初始化它们.

myclass.hpp:

class MyClass {
public:
    MyClass();
    ~MyClass();
private:
    struct Private; unique_ptr<Private> p;
};
Run Code Online (Sandbox Code Playgroud)

myclass.cpp:

#include "myclass.hpp"
#include <string>

struct MyClass::Private {
    int some_var;
    std::string a_string;

    // Option A
    Private() :
        some_var {42},
        a_string {"foo"}
    {}
};

MyClass::MyClass() : p(new MyClass::Private) {
    // Option B
    p->some_var = 42;
    p->a_string = "foo";
}
Run Code Online (Sandbox Code Playgroud)

目前我并没有真正看到两者之间的区别,除非我出于某种原因想要创建新Private对象或复制它们或其他东西,然后选项A可能更好.它还能够初始化初始化列表中的变量,以获得它的价值.但是,我发现选项B往往更具可读性,也许更易于维护.这里有什么东西,我没有看到哪种方式可能会使标尺倾斜?

c++ pimpl-idiom

1
推荐指数
1
解决办法
595
查看次数

使用PIMPL习语,实现是否应始终是该类的私有成员?

我已经看到PIMPL习语以两种不同的方式实现.一种方法是始终使实现成为类的私有成员.这确保了无论如何都无法从类外部访问实现.

例如:

example.h文件

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

class Example {
public:
    void doSomething();
private:
    struct Impl;
    std::shared_ptr<Impl> pimpl_;
};

#endif /* EXAMPLE_H */
Run Code Online (Sandbox Code Playgroud)

Example.cpp

#include "Example.h"
#include <iostream>

struct Example::Impl {
    void doSomething() {
        std::cout << "Hello World!" << std::endl;
    }
};

void Example::doSomething() {
    pimpl_->doSomething();
}
Run Code Online (Sandbox Code Playgroud)

main.cpp中

#include "Example.h"

int main() {
    Example ex;
    ex.doSomething();
    system("pause");
}
Run Code Online (Sandbox Code Playgroud)

我看到的另一种方法是使用它自己的.h文件和自己的.cpp文件使实现成为一个完全独立的类.

例如:

example.h文件

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

struct ExampleImpl;

class Example { …
Run Code Online (Sandbox Code Playgroud)

c++ oop optimization pimpl-idiom dependency-management

1
推荐指数
1
解决办法
453
查看次数

在unique_ptr中显式调用对象的复制构造函数

我正在使用pimpl成语const std::unique_ptr来保存类实现.我的班级需要支持复制构建和复制分配.我想要做的是手动调用impl类中的类的复制构造函数unique_ptr.但是,我没有看到如何.

#include <memory>

struct potato {
    potato();
    ~potato();
    potato(const potato& p);

private:
    struct impl;
    const std::unique_ptr<impl> _pimpl;
};

struct potato::impl {
    int carbs = 42;
};

potato::potato()
        : _pimpl(std::make_unique<impl>()) {
}

potato::~potato() = default;

potato::potato(const potato& p) {
    // Try to call the copy constructor of impl, stored in unique_ptr, not the
    // unique_ptr copy-constructor (which doesn't exist).
    _pimpl.get()->impl(p._pimpl); // This doesn't work.
}
Run Code Online (Sandbox Code Playgroud)

我已经检查了另一个关于在对象上显式调用copy-constructor的问题.一个答案建议使用placement new.

Object dstObject;
new(&dstObject) Object(&anotherObject);
Run Code Online (Sandbox Code Playgroud)

我可以在我的拷贝构造函数中使用它吗?如果是这样,怎么样?我真的不明白那里发生了什么.谢谢.

c++ pimpl-idiom copy-constructor unique-ptr c++11

1
推荐指数
1
解决办法
260
查看次数

如何使用 Pimpl 向公共成员授予访问权限?

  • pimpl.h
#include <memory>

class MyClassImpl;

class MyClass {
    void Foo();

    struct MyStruct {
      int a;
      int b;
    } variable_struct;
private:
    std::unique_ptr<MyClassImpl> m_pImpl;
};
Run Code Online (Sandbox Code Playgroud)
  • pimpl.cpp
class MyClassImpl
{
public:
    void DoStuff() { /*...*/ }
    struct MyStructImpl {
      int a;
      int b;
    } variable_struct_impl;
};


// MyClass (External/User interface)
MyClass::MyClass () : m_pImpl(new MyClassImpl()) { }

MyClass::~MyClass () = default;

void MyClass::Foo() {
    m_pImpl->DoStuff();
}
Run Code Online (Sandbox Code Playgroud)
  • 如何/将实现的公共成员共享给 Pimpl 类(最终用户)的最佳实践是什么?
    • 如果它们有不同的名称(如我的示例中的 和struct MyStruct( struct MyStructImpl/ variable_struct) )怎么办variable_struct_impl

对于方法来说,这是很清楚的,无论如何我们都需要创建前向方法。(示例中 …

c++ pimpl-idiom

1
推荐指数
1
解决办法
358
查看次数

C++ 中的 pImpl 模式需要 impl 子类的完整定义

我已经查看了许多关于 pImpl、unique_ptr 和前向声明的 SO 问题,但无法找出问题所在。答案是std::unique_ptr<T> 需要知道 T 的完整定义吗?看起来很完整,但我不知道如何将其应用到我的情况。

我在https://wandbox.org/permlink/9joq3PnkEZ6j8TI8有一个简单的 pImpl 模式。主类包含 impl 类的 unique_ptr,它管理一些资源,因此它具有自定义构造函数/析构函数。我正在使用带有 clang 14 的 C++20(在 MacOS 上,确实如此,但在上面的 wandbox 中也发生了同样的错误)。

编译它会出现可怕的“对不完整类型无效应用‘sizeof’”错误。

代码是这样的:

// testclass.h
#include <experimental/propagate_const>

// Main class with a pImpl pattern -- impl class is incomplete here.
class Main {
  public:
    void main_f();
    Main();
  private:
    class impl;
    std::experimental::propagate_const<std::unique_ptr<impl>> pImpl;
};
Run Code Online (Sandbox Code Playgroud)
// testclass.cpp
#include "testclass.h"

// Implementation of Main with its impl

class Main::impl {
  private:
    int foo;
 public:
  impl() = default;
  ~impl() { …
Run Code Online (Sandbox Code Playgroud)

c++ pimpl-idiom

1
推荐指数
1
解决办法
53
查看次数

奇怪的"类class :: method():stuff"语法C++

在阅读pImpl成语的一些内容时,我发现了类似这样的东西:

MyClass::MyClass() : pimpl_( new MyClassImp() )

第一:这是什么意思?
第二:语法是什么?
很抱歉这样的菜鸟.

c++ syntax constructor pimpl-idiom

0
推荐指数
1
解决办法
210
查看次数

一种更简单的pimpl形式

为什么不选择这个设计:

// A.hpp
class A
{
public:
    void do_something();
};

// A.cpp
#include "A.hpp"
#include <vector>

std::vector<int> impl_database_for_do_something;

static void impl_helper_for_do_something(const std::vector<int>& database){}

void A::do_something(){ impl_helper_for_do_something(impl_database_for_do_something); }
Run Code Online (Sandbox Code Playgroud)

而不是这一个:

// A.hpp
#include <vector>
class A
{
public:
    void do_something();

private:
    std::vector<int> database_for_do_something_;
    void helper_for_do_something(const std::vector<int>& database){}
};
Run Code Online (Sandbox Code Playgroud)

我可以隐藏实现细节并使用源文件中定义的变量和静态函数加速编译吗?如果没有,这个设计有什么问题(除了继承)?

c++ information-hiding pimpl-idiom interface

0
推荐指数
2
解决办法
330
查看次数

PIMPL习语和复制语义

我正在使用一个库,其中包含许多使用PIMPL习语构建的类.在我看来,我发现不好的是,这些类是使用a std::shared_ptr实现的.这意味着对象实际上是"隐式共享"的.我的问题是:这是实施PIMPL的正确方法吗?或PIMPL和"隐式共享"是两种不同的习语,因此默认情况下不应该混合使用?处理复制语义的正确方法是什么?

c++ pimpl-idiom deep-copy

0
推荐指数
1
解决办法
1248
查看次数

在C++ pimpl中使用void指针的优点和缺点

我正在尝试研究C++的pimpl技术.在网上浏览了一些文章后,我发现pimpl有两种不同的方式,一种是

class X
{
  public:
    X(...parameters...)
    ~X()
  private:
    struct Impl;
    Impl* impl_;
};
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用原始的void指针,就像

class X
{
  public:
    X(...parameters...)
    ~X()
  private:
    void * impl_;
};
Run Code Online (Sandbox Code Playgroud)

然后使用static_cast将void指针强制转换回原始类型.

比较这两种方式有哪些优缺点?

谢谢!

c++ pimpl-idiom

0
推荐指数
1
解决办法
670
查看次数

执行 PIMPL 时如何避免共享指针开销

AFAIKunique_ptr与 PIMPL 一起使用非常棘手,因为删除器是unique_ptr类型的一部分,因此它不适用于不完整的类型。另一方面,shared_ptr使用动态删除器,因此它可以处理不完整的类型。

shared_ptr无论我是否需要,都存在给我原子操作的性能问题。

我可以使用其他更快的替代方案吗std::?我显然对类型擦除很满意,我说的是原子引用计数的成本。

#include <any>
#include <memory>
#include <iosfwd>

std::shared_ptr<std::fstream> sp;
// unique_ptr requires complete type
// std::unique_ptr<std::fstream> up;
std::any a;

#include <fstream>
int main() {
    // any requires copy_constructible
    // a = std::fstream{};  
    sp = std::make_shared<std::fstream>();
} 
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我考虑过any,但它不适用于某些类型。
  • 我考虑使用动态删除器的 unique_ptr ,但据我所知构造unique_ptr函数永远不会“告诉”删除器构造的对象是什么(为删除器提供一种学习如何销毁对象的方法)。

PS 我知道很久以前boost::shared_ptr就有宏来禁用原子引用计数,但即使仍然支持我也不想切换到boost::shared_ptr.

c++ pimpl-idiom shared-ptr c++20

-1
推荐指数
1
解决办法
285
查看次数