22.使用Pimpl惯用法时,将特殊成员函数的定义放在实现文件中-创新互联
智能指针与Impl惯用法
文章名称:22.使用Pimpl惯用法时,将特殊成员函数的定义放在实现文件中-创新互联
浏览路径:http://pwwzsj.com/article/docjoj.html
- 不使用impl惯用法会怎样?
- impl惯用法如何实现?
- 使用std::unique_ptr
- 使用unique_ptr还是shared_ptr
- 为什么同样是智能指针却有如此大的差别?
class widget
{public:
widget();
~widget();
private:
std::string name;
std::vectordata;
};
- string和vector的头文件必须在声明widget的头文件中声明
- 第一步中的头文件增加了widget用户的编译时间
- widget的客户也依赖于第一步中的头文件,如果某个头文件中的内容发生了变化,则widget的客户必须重新编译
#pragma once
class widget
{public:
widget();
~widget();
private:
struct Impl;
struct Impl *pImpl;
};
#include "widget.h"
#include#include#include "Gadget.h"
struct widget::Impl {std::string name;
std::vectordata;
Gadget g;
};
widget::widget()
:pImpl(new Impl())
{}
widget::~widget()
{delete pImpl;
}
- 没有了上述性能问题,但是代码陈旧
- 对于不完整类型我们可以声明他的指针
#pragma once
#includeclass widget
{public:
widget();
~widget();
widget(const widget& rhs);
widget &operator=(const widget& rhs);
widget(widget &&rhs);
widget &operator=(widget &&rhs);
private:
struct Impl;
std::unique_ptrpImpl;
};
#include "widget.h"
#include#include#include "Gadget.h"
struct widget::Impl {std::string name;
std::vectordata;
Gadget g;
};
widget::widget()
:pImpl(std::make_unique())
{}
widget::~widget() = default;
widget::widget(const widget & rhs)
:pImpl(std::make_unique(*rhs.pImpl))
{}
widget &widget::operator=(const widget& rhs)
{*pImpl = *rhs.pImpl;
return *this;
}
widget::widget(widget && rhs) = default;
widget &widget::operator=(widget &&rhs) = default;
- 注意我们声明了一个空的析构函数,这是必须的。原因:对象作用域结束后,对象析构->析构unique_ptr->static_cast§->delete,位于static_cast的时候需要保证p是完整类型。
- 我们显式声明了移动操作,原在widget中声明的析构函数会阻止编译器生成移动操作,并且移动操作同样需要看到完整类型定义。
a.编译器的移动赋值操作需要在重新赋值前析构pImpl指向的对象
b.编译器会在移动构造函数内抛出异常的时候生成析构pImpl的代码,而pImpl的析构函数要求Impl具有完整类型 - 我们显式定义了自己的拷贝构造函数以及重写了赋值运算符函数(std::make_unique(Impl impl)是值拷贝),因为由于std::unique_ptr的存在,编译器不会声明默认版本
在不进行资源共享的前提下,为达到实现PImpl惯用法的目的,应该选择使用std::unique_ptr智能指针,因为对象内部的pImpl指针拥有相应实现对象的专属所有权。(含义清晰,但是编码复杂)
#pragma once
#includeclass widget
{public:
widget();
widget(const widget& rhs);
widget &operator=(const widget& rhs);
private:
struct Impl;
std::shared_ptrpImpl;
};
#include "widget.h"
#include#include#include "Gadget.h"
struct widget::Impl {std::string name;
std::vectordata;
Gadget g;
};
widget::widget()
:pImpl(std::make_shared())
{}
widget::widget(const widget & rhs)
:pImpl(std::make_shared(*rhs.pImpl))
{}
widget &widget::operator=(const widget& rhs)
{*pImpl = *rhs.pImpl;
return *this;
}
为什么同样是智能指针却有如此大的差别?- 对std::unique_ptr而言,析构器类型是智能指针类型的一部分,这会使得编译器产生更小尺寸的运行期数据结构以及更快的运行期代码,带来的后果就是,要是使用编译器生成的特殊函数(例如拷贝或者移动)就要求器指向的类型必须是完整类型。
- 对std::shared_ptr而言,析构器的类型并不是智能指针的一部分,这回使得编译器产生更大更慢的代码,但是,获得的好处是在使用编译器生成的特殊函数时,其指向的类型不要求时完整类型
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
文章名称:22.使用Pimpl惯用法时,将特殊成员函数的定义放在实现文件中-创新互联
浏览路径:http://pwwzsj.com/article/docjoj.html