[英]Pimpl idiom, separate interface/implementation files, and multiple virtual inheritance. How?
以下标头和实现文件结合了所有三个概念,但无法编译:
$ cat a.h
#include <memory>
class Base {
protected:
class BaseImpl;
std::shared_ptr<BaseImpl> pImpl;
Base(BaseImpl* impl)
: pImpl{impl}
{}
public:
virtual ~Base()
{}
virtual void func() =0;
};
class Der : public virtual Base {
private:
class DerImpl;
DerImpl* getPimpl() const noexcept;
public:
Der();
virtual ~Der()
{}
void func();
};
$ cat a.cpp
#include "a.h"
class Base::BaseImpl {
};
class Der::DerImpl : public virtual Base::BaseImpl {
public:
void func() {}
};
Der::Der()
: Base{new DerImpl()}
{}
Der::DerImpl* Der::getPimpl() const noexcept {
return static_cast<DerImpl*>(pImpl.get());
}
void Der::func() {
getPimpl()->func();
}
$ g++ --std=c++11 -c a.cpp
a.cpp: In member function ‘Der::DerImpl* Der::getPimpl() const’:
a.cpp:16:45: error: cannot convert from base ‘Base::BaseImpl’ to derived type ‘Der::DerImpl’ via virtual base ‘Base::BaseImpl’
return static_cast<DerImpl*>(pImpl.get());
^
您能否告诉我什么地方出了问题,为什么发生以及如何使用Pimpl习惯用语和多重虚拟继承创建单独的声明和定义文件。
多个虚拟基类问题和pimpl习语是完全不同的主题。 pimpl和impl通常位于单独的头文件和cpp文件中。 整个pimpl习惯用法是一个指向实现的指针,这意味着pimple封装了impl的功能以将其公开给客户端,并且仅包含指向该impl的void指针,该指针已转换为pimpl的cpp文件中的实际impl,并创建并销毁了该指针。在它的构造函数和解构函数中。 如果愿意,还可以实现更多功能,但是这种惯用法通常用于防止知识产权在二进制dll附带的头文件中公开。 它还可以巩固您的界面,以便您不会在将来的产品发行版中意外破坏客户端应用程序。
它用于库共享,而不是单个应用程序的大量生产代码。 至于基类和继承,您的基类有一个指向其自身实例的指针。 让我们一次专注于一件事。 这应该有助于为您详细说明pimpl习惯用法。 如果您的产品需要多个虚拟继承,并且它是一个库,则可以根据需要使用pimpl惯用语公开它。 如果您需要有关多重继承的复习,可以在这里找到。
编译器错误告诉您不能static_cast
从派生类型转换为基本类型。 使用dynamic_cast
或reinterpret_cast
( reinterpret_cast
更快,但不能保证在多重继承下正常工作)。
ps另外,不要忘记虚拟基类的虚拟析构函数。
我设法通过以下方式组合了概念(带有单独文件和多个虚拟继承的pImpl)
pImpl
保存一个指向void
的指针,而不是一个实现的指针,以避免基本构造函数修改指针; 和 getPimpl()
中使用reinterpret_cast
而不是static_cast
来解决在虚拟继承环境中无法向下转换的问题。 这是代码和测试例程:
$ cat a.h
#include <memory>
class Base {
protected:
class Impl;
std::shared_ptr<void> pImpl;
Base(void* impl) : pImpl{impl} {}
public:
virtual ~Base() =0;
};
class Der1 : public virtual Base {
protected:
class Impl; // Won't compile if `private`
private:
Impl* getPimpl() const noexcept;
public:
Der1();
virtual ~Der1() {}
void der1Func();
};
class Der2 : public virtual Base {
protected:
class Impl; // Won't compile if `private`
private:
Impl* getPimpl() const noexcept;
public:
Der2();
virtual ~Der2() {}
void der2Func();
};
class Joined : public Der1, public Der2 {
private:
class Impl;
Impl* getPimpl() const noexcept;
public:
Joined();
void joinedFunc();
};
$ cat a.cpp
#include "a.h"
#include <iostream>
class Base::Impl {};
class Der1::Impl : public virtual Base::Impl {
public:
void der1Func() {
std::cout << "Der1::Impl::der1Func() called\n";
}
};
class Der2::Impl : public virtual Base::Impl {
public:
void der2Func() {
std::cout << "Der2::Impl::der2Func() called\n";
}
};
class Joined::Impl : public virtual Der1::Impl, public virtual Der2::Impl {
public:
void joinedFunc() {
std::cout << "Joined::Impl::joinedFunc() called\n";
}
};
Base::~Base() {
reinterpret_cast<Impl*>(pImpl.get())->~Impl();
}
Der1::Der1() : Base{new Impl()} {}
Der1::Impl* Der1::getPimpl() const noexcept {
return reinterpret_cast<Impl*>(pImpl.get());
}
void Der1::der1Func() {
getPimpl()->der1Func();
}
Der2::Der2() : Base{new Impl()} {}
Der2::Impl* Der2::getPimpl() const noexcept {
return reinterpret_cast<Impl*>(pImpl.get());
}
void Der2::der2Func() {
getPimpl()->der2Func();
}
Joined::Joined() : Base{new Impl()} {}
Joined::Impl* Joined::getPimpl() const noexcept {
return reinterpret_cast<Impl*>(pImpl.get());
}
void Joined::joinedFunc() {
getPimpl()->joinedFunc();
}
int main() {
Joined* joined = new Joined();
joined->joinedFunc(); // Calls Der1::joinedFunc()
joined->der1Func(); // Calls Der1::der1Func()
joined->der2Func(); // Calls Der2::der2Func()
Der1* der1 = joined;
der1->der1Func(); // Calls Der1::der1Func()
}
$ g++ --std=c++11 a.cpp && ./a.out
Joined::Impl::joinedFunc() called
Der1::Impl::der1Func() called
Der2::Impl::der2Func() called
Der1::Impl::der1Func() called
$
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.