请参阅我的PIMPL实现与继承。在派生类中,DerivedImpl继承自BaseImpl。
问:是否应仅在基类中定义指向Impl的指针,如以下代码?如果是这样,每次我需要使用基本指针时,都必须将其强制转换为派生类型。但是,根据分析结果,静态强制转换shared_ptr看起来很昂贵,因为该强制转换已被广泛使用。并且强制转换功能不能在标头中内联,因为它在那里不完整。
也许我犯了一些错误。还是使用智能指针对此有更好的实现?
// Base.h
class BaseImpl; // pre-declaration
class Base
{
public:
Base();
explicit Base(BaseImpl* ptr);
~Base();
protected:
std::shared_ptr<BaseImpl> d_Ptr;
};
// baseimpl.h
class BaseImpl
{
double mDate;
};
// Derived.h
#include "Base.h"
class DerivedImpl;
class Derived :
public Base
{
public:
Derived();
~Derived();
std::shared_ptr<DerivedImpl> d_func();
const std::shared_ptr<DerivedImpl> d_func() const;
};
// Derived.cpp
#include "Derived.h"
#include "DerivedImpl.h"
Derived::Derived() : Base(new DerivedImpl())
{
}
Derived::~Derived()
{
}
std::shared_ptr<DerivedImpl> Derived::d_func()
{
return std::static_pointer_cast<DerivedImpl>(d_Ptr);
}
const std::shared_ptr<DerivedImpl> Derived::d_func() const
{
return std::static_pointer_cast<DerivedImpl>(d_Ptr);
}
我假设您确实想要您所描述的内容,以实现细节为模:
公共类的继承层次结构。
基于实现类的相应继承层次结构。
该实现对全局名称空间和/或宏的可能使用,应限于单独编译的单元。
这是一个问题,派生类特定于初始化,例如在C ++类中包装一组低级GUI小部件时弹出。还有其他许多情况。可能的解决方案范围很广,但到目前为止的解决方案是通过基类构造函数将指向实现的指针传递到最顶层的基类,然后将其提供给派生类。
不过,您不确定那是个好主意:
”是否应该仅在基类中定义指向Impl的指针,如以下代码?
是的,理想情况下应该如此,因为这种方法可确保始终完整构建可用的基类实例。这就是C ++构造函数的基本思想。初始化之后(例如,基类子对象的初始化),您手头有一个工作对象,或者什么也没有,即异常或终止。
但是,此方法为您解决了两个问题:
如何有效地提供派生类的实现指针?
如何从基类实现中派生一个实现?
通过为实现使用单独的头文件,可以轻松解决后一个问题。请记住,信息隐藏的目的不是要使这些类的源代码在物理上是不可访问的,尽管仍然可以做到。但是要避免污染全局名称空间和宏域。
第一个问题,这是您真正要问的,
”静态根据一个shared_ptr看上去很昂贵转换为剖析的结果,因为这个转换是广泛使用
真的不是问题。
向下转换功能只需要在代码的实现部分中就可以访问,那里的源代码可用,并且可以内联调用。
最后,只是建议,您应该对实现指针不使用unique_ptr
,也可以不使用智能指针,或者使用自动克隆的智能指针而不是shared_ptr
。因为您通常不希望公共类实例的副本,所以可以与原始实例共享其实现。除了实现没有状态的情况之外,在这种情况下动态分配它不是很有意义。
例子:
Base.hpp:#pragma once
#include <memory>
namespace my {
using std::unique_ptr;
class Base
{
protected:
class Impl;
private:
unique_ptr<Impl> p_impl_;
protected:
auto p_impl() -> Impl* { return p_impl_.get(); }
auto p_impl() const -> Impl const* { return p_impl_.get(); }
Base( unique_ptr<Impl> p_derived_impl );
public:
auto foo() const -> char const*;
~Base();
Base();
};
} // namespace my
Base.Impl.hpp:
#pragma once
#include "Base.hpp"
class my::Base::Impl
{
public:
auto virtual foo() const -> char const* { return "Base"; }
virtual ~Impl() {}
};
Base.cpp:
#include "Base.Impl.hpp"
#include <utility> // std::move
using std::move;
using std::unique_ptr;
auto my::Base::foo() const
-> char const*
{ return p_impl()->foo(); }
my::Base::~Base() {}
my::Base::Base()
: p_impl_( new Impl() )
{}
my::Base::Base( unique_ptr<Impl> p_derived_impl )
: p_impl_( move( p_derived_impl ) )
{}
Derived.hpp:
#pragma once
#include "Base.hpp"
namespace my {
class Derived
: public Base
{
protected:
class Impl;
Derived( unique_ptr<Impl> p_morederived_impl );
private:
auto p_impl() -> Impl*;
auto p_impl() const -> Impl const*;
public:
~Derived();
Derived();
};
} // namespace my
派生的Impl.hpp:
#pragma once
#include "Base.Impl.hpp"
#include "Derived.hpp"
class my::Derived::Impl
: public my::Base::Impl
{
public:
auto foo() const -> char const* override { return "Derived"; }
};
Derived.cpp:
#include "Derived.Impl.hpp"
#include <utility> // std::move
using std::move;
using std::unique_ptr;
inline auto my::Derived::p_impl() -> Impl*
{ return static_cast<Impl*>( Base::p_impl() ); }
inline auto my::Derived::p_impl() const -> Impl const*
{ return static_cast<Impl const*>( Base::p_impl() ); }
my::Derived::~Derived() {}
my::Derived::Derived()
: Base( unique_ptr<Impl>( new Impl() ) )
{}
my::Derived::Derived( unique_ptr<Impl> p_morederived_impl )
: Base( move( p_morederived_impl ) )
{}
main.cpp:
#include "Derived.hpp"
#include <iostream>
using namespace std;
auto main() -> int
{
wcout << my::Derived().foo() << endl;
}
技术性:在类中Derived
,downcaster函数为private
,以防止更多派生类直接使用它们。这是因为实现是inline
,并且在使用它们的每个翻译单元中都应定义相同。与其将其划分为更多的标头,不如将更多的派生类应该/可以直接从Base
实现中强制转换下来Derived
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句