使用 pimpl 习语时 c++ 中的构造函数和析构函数
Constructor and destructor in c++ when using the pimpl idiom
我来自 Java,它有不同的方式来处理关于 class 实现的私有和必须隐藏的内容,它还有一个垃圾收集器,这意味着不需要析构函数。
我学习了如何在 C++ 中实现 class 的基础知识,但我需要更好地理解如何在使用 pimpl 惯用语时实现 class,尤其是构造函数和析构函数。
hpp 文件:
class MyClass{
public:
MyClass();
MyClass(std::vector<int>& arr);
~MyClass();
private:
struct Impl;
Impl* pimpl;
};
cpp 文件:
#include "MyClass.hpp"
using namespace std;
struct MyClass::Impl{
vector<int> arr;
int var;
};
我写了一个 class 的示例代码,我需要继续使用 pimpl idiom(这意味着我不能改变 pimpl idiom 的使用方式),我正在寻找答案对于这些问题:
- 如何实现构造函数来创建 MyClass 的空实例?
MyClass::MyClass(){}
- 如何实现构造函数以使用这些参数创建 MyClass 的实例?
MyClass::MyClass(vector<int>& arr,int var){}
- 如何实现析构函数?
MyClass::~MyClass(){}
编辑:
我会怎么做:
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;
}
这是在现代 C++ 中实现 PIMPL 习语的正确方法示例:
foo.hpp
#pragma once
#include <memory>
class Foo {
public:
Foo();
~Foo();
Foo(Foo const &) = delete;
Foo &operator=(Foo const &) = delete;
Foo(Foo &&) noexcept;
Foo &operator=(Foo &&) noexcept;
void bar();
private:
class impl;
std::unique_ptr<impl> pimpl_;
};
foo.cpp:
#include "foo.hpp"
#include <iostream>
class Foo::impl {
public:
impl() = default;
~impl() = default;
impl(impl const &) = default;
impl &operator=(impl const &) = default;
impl(impl &&) noexcept = default;
impl &operator=(impl &&) noexcept = default;
void bar() { std::cout << "bar" << std::endl; }
};
Foo::Foo() : pimpl_(new impl{}) {}
Foo::~Foo() = default;
Foo::Foo(Foo &&) noexcept = default;
Foo &Foo::operator=(Foo &&) noexcept = default;
void Foo::bar() { pimpl_->bar(); }
main.cpp:
#include "foo.hpp"
int main(int argc, char const *argv[]) {
Foo foo;
foo.bar();
return 0;
}
有几句话要说:
- 使用智能指针(唯一或共享)保存对 'impl' 对象的引用。它会像 JAVA 一样帮助您控制对基础对象的引用数量。在此示例中,由于 Foo class 消失了 'impl' 自动销毁,因此无需手动观察 'impl' 对象
的生命周期
- 'impl' 必须始终在 *.cpp 文件(或一堆文件)或内部 *.hpp and/or *.cpp 文件中完全声明和定义,以便您前面的用户 class(即在本例中 'Foo')聚合 'impl' 不可能看到你的 'impl' class 的任何变化和内容,这就是 PIMPL 习语存在的原因: 为用户保留前面 class 的界面,所有更改大部分应在隐藏 'impl' class
中完成
- 前面class的析构函数必须始终在*.cpp文件中定义,因为编译器必须知道如何销毁从*.hpp文件中看不到的'impl'前面class被声明
- 具有 rvalue-references 的特殊函数(move-ctor 和 move-assignment 运算符)也必须在 *.cpp 文件中定义,原因与前一节相同
我来自 Java,它有不同的方式来处理关于 class 实现的私有和必须隐藏的内容,它还有一个垃圾收集器,这意味着不需要析构函数。
我学习了如何在 C++ 中实现 class 的基础知识,但我需要更好地理解如何在使用 pimpl 惯用语时实现 class,尤其是构造函数和析构函数。
hpp 文件:
class MyClass{
public:
MyClass();
MyClass(std::vector<int>& arr);
~MyClass();
private:
struct Impl;
Impl* pimpl;
};
cpp 文件:
#include "MyClass.hpp"
using namespace std;
struct MyClass::Impl{
vector<int> arr;
int var;
};
我写了一个 class 的示例代码,我需要继续使用 pimpl idiom(这意味着我不能改变 pimpl idiom 的使用方式),我正在寻找答案对于这些问题:
- 如何实现构造函数来创建 MyClass 的空实例?
MyClass::MyClass(){}
- 如何实现构造函数以使用这些参数创建 MyClass 的实例?
MyClass::MyClass(vector<int>& arr,int var){}
- 如何实现析构函数?
MyClass::~MyClass(){}
编辑:
我会怎么做:
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;
}
这是在现代 C++ 中实现 PIMPL 习语的正确方法示例:
foo.hpp
#pragma once
#include <memory>
class Foo {
public:
Foo();
~Foo();
Foo(Foo const &) = delete;
Foo &operator=(Foo const &) = delete;
Foo(Foo &&) noexcept;
Foo &operator=(Foo &&) noexcept;
void bar();
private:
class impl;
std::unique_ptr<impl> pimpl_;
};
foo.cpp:
#include "foo.hpp"
#include <iostream>
class Foo::impl {
public:
impl() = default;
~impl() = default;
impl(impl const &) = default;
impl &operator=(impl const &) = default;
impl(impl &&) noexcept = default;
impl &operator=(impl &&) noexcept = default;
void bar() { std::cout << "bar" << std::endl; }
};
Foo::Foo() : pimpl_(new impl{}) {}
Foo::~Foo() = default;
Foo::Foo(Foo &&) noexcept = default;
Foo &Foo::operator=(Foo &&) noexcept = default;
void Foo::bar() { pimpl_->bar(); }
main.cpp:
#include "foo.hpp"
int main(int argc, char const *argv[]) {
Foo foo;
foo.bar();
return 0;
}
有几句话要说:
- 使用智能指针(唯一或共享)保存对 'impl' 对象的引用。它会像 JAVA 一样帮助您控制对基础对象的引用数量。在此示例中,由于 Foo class 消失了 'impl' 自动销毁,因此无需手动观察 'impl' 对象 的生命周期
- 'impl' 必须始终在 *.cpp 文件(或一堆文件)或内部 *.hpp and/or *.cpp 文件中完全声明和定义,以便您前面的用户 class(即在本例中 'Foo')聚合 'impl' 不可能看到你的 'impl' class 的任何变化和内容,这就是 PIMPL 习语存在的原因: 为用户保留前面 class 的界面,所有更改大部分应在隐藏 'impl' class 中完成
- 前面class的析构函数必须始终在*.cpp文件中定义,因为编译器必须知道如何销毁从*.hpp文件中看不到的'impl'前面class被声明
- 具有 rvalue-references 的特殊函数(move-ctor 和 move-assignment 运算符)也必须在 *.cpp 文件中定义,原因与前一节相同