繁体   English   中英

Pimpl习惯用语,单独的接口/实现文件以及多个虚拟继承。 怎么样?

[英]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_castreinterpret_castreinterpret_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM