2017-02-12 5 views
0

следующий заголовок и реализация файлов сочетает в себе все три понятия, но не компилируется:Идиома Pimpl, отдельные файлы интерфейса/реализации и несколько виртуальных наследований. Как?

$ 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 и несколько виртуальных наследований.

+0

http://stackoverflow.com/questions/7484913/why-cant-static-cast-be-used-to-down-cast- когда-virtual-inheritance-участвует –

+0

@MM Вот и все. Благодарю. –

ответ

0

Множество вопросов виртуальных базовых классов и идиомы pimpl - это совершенно разные темы. pimpl и impl, как правило, находятся в отдельных заголовках и файлах cpp. Вся идиома pimpl является указателем на реализацию, а это означает, что прыщ обертывает функции impl, чтобы подвергать их клиенту и содержит только указатель void на имп, который отбрасывается фактическим импортом в файл cpp pimpl, и создает и уничтожает этот в его конструкторах и деконструкторах. Вы также можете реализовать больше функциональных возможностей, если хотите, но этот тип идиомы обычно используется для предотвращения распространения интеллектуальной собственности в файлах заголовков, поставляемых с бинарной dll. Он также может упростить ваш интерфейс, чтобы вы случайно не разбивали клиентские приложения в будущих выпусках вашего продукта.

Это для обмена библиотекой, а не столько кода производства одного приложения. Что касается базовых классов и наследования, ваш базовый класс имеет указатель на экземпляр своего «я». Давайте сосредоточимся на одной вещи за раз. This должен служить, чтобы помочь объяснить идиому pimpl в деталях для вас. Если вам нужно несколько виртуальных наследований для вашего продукта, и это библиотека, просто выложите ее, используя идиом pimpl, если хотите. Если вам нужно обновить множественное наследование, вы можете найти его here.

0

Ошибка компилятора говорит, что вы не можете static_cast от производного типа до базового типа. Используйте либо dynamic_cast, либо reinterpret_cast (reinterpret_cast быстрее, но не гарантирует правильную работу с множественным наследованием).

p.s. Кроме того, не забывайте о виртуальных деструкторах для виртуальных базовых классов.

+0

'dynamic_cast' не работал, а' reinterpret_cast' не является опцией в предполагаемой множественной среде наследования (это вызвало SIGSEGV). –

+0

@SteveEmmerson, RTTI? Если это так, 'dynamic_cast' должен работать. –

+0

Не знаю. Однако проблема в том, что я нашел обходное решение, которое более эффективно, чем использование 'dynamic_cast'. –

0

мне удалось объединить понятия (Pimpl с отдельными файлами и множественным виртуальным наследованием) с помощью

  • Сохранения указателя на void в pImpl, а не указатель на реализацию, чтобы избежать модификаций указателя на базе конструктор; и
  • Использование reinterpret_cast, а не static_cast в getPimpl() в производных классах для обхода невозможности сглаживания в виртуальной среде наследования.

Вот код и тест рутина:

$ 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 
$ 

 Смежные вопросы

  • Нет связанных вопросов^_^