C ++ Как я могу получить эту структуру класса? - PullRequest
5 голосов
/ 20 сентября 2011

Я ломаю голову, пытаясь выяснить, как писать кроссплатформенные классы, избегая при этом стоимости виртуальных функций и любого рода уродства в версиях классов, специфичных для платформы.Вот что я попробовал.

PlatformIndependantClass.hpp

class PlatformIndependantClass {
    public:
        PlatformIndependantClass();
        std::string GetPlatformName();
    private:
        PlatformIndependantClass* mImplementation;

};

LinuxClass.hpp

#include "PlatformIndependantClass.hpp"
class LinuxClass : public PlatformIndependantClass{
    public:
        std::string GetPlatformName();
};

WindowsClass.hpp

#include "PlatformIndependantClass.hpp"
class WindowsClass : public PlatformIndependantClass {
    public:
        std::string GetPlatformName();
};

PlatformIndependantClass.cpp

#include "PlatformIndependantClass.hpp"
#include "LinuxClass.hpp"
#include "WindowsClass.hpp"
PlatformIndependantClass::PlatformIndependantClass() {
    #ifdef TARGET_LINUX
        mImplementation = new LinuxClass();
    #endif
    #ifdef TARGET_WINDOWS
        mImplementation = new WindowsClass();
    #endif
}
std::string PlatformIndependantClass::GetPlatformName() {
    return mImplementation->GetPlatformName();
}

LinuxClass.cpp

#include "LinuxClass.hpp"
std::string LinuxClass::GetPlatformName() {
    return std::string("This was compiled on linux!");
}

WindowsClass.cpp

#include "WindowsClass.hpp"
std::string WindowsClass::GetPlatformName() {
    return std::string("This was compiled on windows!");
}

main.cpp

#include <iostream>
#include "PlatformIndependantClass.hpp"

using namespace std;

int main()
{
    PlatformIndependantClass* cl = new PlatformIndependantClass();
    cout << "Hello world!" << endl;
    cout << "Operating system name is: " << cl->GetPlatformName() << endl;
    cout << "Bye!" << endl;
    return 0;
}

Теперь, это скомпилируется нормально, но я получаю ошибку сегментации,Я считаю, что это потому, что классы, специфичные для платформы, наследуются от PlatformIndependantClass, который при создании создает экземпляр класса, специфичного для платформы, поэтому я получаю бесконечную рекурсию.Каждый раз, когда я пытаюсь, я просто запутываюсь!

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

Ответы [ 6 ]

6 голосов
/ 20 сентября 2011

Я думаю, что то, что вы пытаетесь достичь, может быть достигнуто гораздо проще ...

Object.h:

#include <normal includes>

#if WINDOWS
#include <windows includes>
#endif

#if LINUX
#include <linux includes>
#endif

class Object
{
private:

#if WINDOWS
//Windows Specific Fields...
#endif

#if LINUX
//Linux Specific Fields...
#endif

public:
    //Function that performs platform specific functionality
    void DoPlatformSpecificStuff();

    //Nothing platform specific here
    void DoStuff();      
};

Object.cpp

#include "Object.h"

void Object::DoStuff() { ... }

ObjectWin32.cpp

#if WINDOWS

#include "Object.h"

void Object::DoPlatformSpecificStuff() 
{ 
    //Windows specific stuff... 
}

#endif

ObjectLinux.cpp

#if LINUX

#include "Object.h"

void Object::DoPlatformSpecificStuff() 
{ 
    //Linux specific stuff... 
}

#endif

И так далее. Я думаю, что это может сделать то, что вы пытаетесь, немного проще. Также не требуется никаких виртуальных функций.

6 голосов
/ 20 сентября 2011

Начиная с конца, да, действительно ужасная идея, как и большинство идей, начинающихся с "Я хочу избежать стоимости виртуальных функций".

Относительно того, почему вы получаете ошибку сегментации (особенно переполнение стека), это потому, что вы используете не виртуальные функции, а статическое связывание. Компилятор не знает, что mImplementation - это не что иное, как PlatformIndependantClass, поэтому, когда вы пытаетесь вызвать return mImplementation->GetPlatformName(), вы вызываете одну и ту же функцию снова и снова.

То, что вы достигли, называется shadowing , вы используете разрешение функции времени компиляции. Компилятор вызовет GetPlatformName функцию фактического типа переменной, которую вы вызываете из , так как нет виртуальной таблицы, которая бы перезаписывала указатели на фактические функции. Поскольку mImplementation равно PlatformIndependantClass, mImplementation->GetPlatformName всегда будет PlatformIndependantClass::GetPlatformName.

Редактировать: Конечно, возникает вопрос, зачем вам нужно одновременно создавать копию своего движка для Windows и Linux. Вы никогда не будете использовать их оба одновременно, верно?

Так почему бы просто не иметь две разные библиотеки, по одной для каждой системы, и связать нужную из вашего make-файла. Вы получаете лучшее из всех миров!

1 голос
/ 20 сентября 2011

Если вы хотите иметь полиморфное поведение без каких-либо накладных расходов во время выполнения, вы можете попробовать странно повторяющийся шаблон (CRTP) . Базовый класс является шаблоном, а производный класс использует себя в качестве параметра шаблона для базы. Это требует, чтобы ваши классы были определены как шаблоны, что еще больше ограничивает их полную реализацию в файлах заголовка (.hpp).

Я не уверен, как применить шаблон в вашем конкретном случае.

1 голос
/ 20 сентября 2011

Вместо использования конструктора для построения экземпляра, специфичного для платформы, я бы создал статический метод фабрики для создания экземпляров:

PlatformIndependantClass* PlatformIndependantClass::getPlatformIndependantClass() {
    #ifdef TARGET_LINUX
        return new LinuxClass();
    #endif
    #ifdef TARGET_WINDOWS
        return new WindowsClass();
    #endif
}

Таким образом вы избегаете рекурсии, а также нетнужен ваш указатель mImplementation.

Я бы также попытался избежать платформо-зависимых классов, но это другая история:)

0 голосов
/ 20 сентября 2011

Вы правы насчет бесконечного цикла.Исправление на самом деле проще, чем вы думаете.

PlatformIndependantClass.hpp

#include //portable headers
struct PlatformDependantClass; //defined in Cpp file
class PlatformIndependantClass {
    public:
        PlatformIndependantClass();
        ~PlatformIndependantClass();
        std::string GetPlatformName();
    private:
        std::unique_ptr<PlatformDependantClass> mImplementation; //note, different type

};

LinuxClass.cpp

#ifdef __GNUC__ 
#include //linux headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //linux only stuff
     //stuff
};    

PlatformIndependantClass() {
    mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
    return std::string("This was compiled on linux!");
}
#endif //__GNUC__ 

WindowsClass.cpp

#ifdef _MSC_VER
#include //windows headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass {  //windows only stuff
     //stuff
};    

PlatformIndependantClass() {
    mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
    return std::string("This was compiled on Windows!");
}
#endif //_MSC_VER

Здесь определен только класс ONE .В Windows он только компилирует и содержит Windows, а в Linux он только компилирует и содержит Linux.Обратите внимание, что void* вещь называется «непрозрачный указатель» или «идиома pimpl» http://en.wikipedia.org/wiki/Opaque_pointer

0 голосов
/ 20 сентября 2011

Я не думаю, что конструктор вызывает бесконечную рекурсию. Это функция GetPlatformName (). Поскольку он не установлен как виртуальный, он может вызывать только сам себя.

Два решения: сделать эту функцию виртуальной или полностью отказаться от наследования.

В любом случае, стоимость функции, вызывающей только другую функцию, будет дороже, чем использование виртуальных функций. Поэтому я бы сказал, сохранить наследование и виртуализировать функции, специфичные для платформы, и вызывать их напрямую, без прохождения через функцию базового класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...