2016-04-30 5 views
0

Учитывайте ситуацию при создании кросс-платформенного многопоточного приложения.Использовать экземпляр абстрактного класса в кросс-платформенном коде

Итак, когда вы это сделаете, вы, вероятно, разделите функциональность платформы между некоторыми классами. Например, вам нужна простая консоль для вывода некоторого текста в ваше приложение. Возможный способ сделать это - объявить некоторый абстрактный класс Console, а затем, когда вам это нужно на какой-то платформе, наследовать от него и использовать экземпляры производных классов.

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

Вот сложный пример.

При разработке кросс-платформенного многопоточного приложения вам нужно что-то вроде Syncer (Synchronizer). Аналогом этого является объект mutex в Windows API.

Вы знаете, что в зависимости от платформы этот класс будет отличаться. Но вы знаете, что есть некоторые методы, которые будет иметь любой вариант платформы. Например, эти методы: lock и unlock. Как вы не знаете, как будут эти методы можно сделать, вы объявляете их чисто виртуальными, как в следующем:

class Syncer 
{ 
public: 
    virtual void lock()=0; 
    virtual void unlock()=0; 
} 

И теперь, развивая некоторый класс, вы понимаете, что вам не нужно одно, но многое из этого Syncer -s. Но вы не можете этого сделать, поскольку класс абстрактный.

Возможные решения, которые я нашел являются:

1) Использование шаблонов. Например:

template<class syncer> 
class SyncUser 
{ 
public: 
    syncer syncInstance; 
    SyncUser() 
    { 
     syncInstance().lock; 
} 

Но проблема в том, что пользователь может вставить класс вместо syncer параметра в любой другой класс (бы он или нет, это не имеет значения, он может это сделать.).. Таким образом, решение не так хорошо, как ожидается общее решение.

2) Использовать макросы. При использовании таким образом, содержание Syncer.h будет, как в следующем:

class Syncer 
{ 
public: 
    virtual void lock()=0; 
    virtual void unlock()=0; 
} 
#ifdef PLATFORM1 
#include "platform1_sync_implement.h" 
#else 
#include "platform2_sync_implement.h" 
#endif 

когда файл "platform1_sync_implement.h" имеет следующее:

#include "Syncer.h" 

class SyncPlatform1 : public Syncer 
{ 
    //Correct implementation 
} 

#define Syncer SyncPlatform1 

Теперь, кто включен Syncer.h бы иметь реализованный вариант. Но этот способ также не очень хорош, так как требуется список платформ, где будет реализована абстракция Sync. Это не так хорошо для больших проектов, так как вам просто нужно объявить этот класс и заботиться о том, чтобы другие программисты могли его использовать.

Итак, вопрос: есть ли лучший способ использовать «экземпляр» абстрактного класса, зная, что его дочерний класс будет реализован где-то.

P.С .: Я знаю, что это не лучшее имя для такого вопроса. P.P.S .: Извините за плохой английский.

+0

tl; dr .. короткий ответ - нет «экземпляра абстрактного класса». По определению абстрактные классы не могут быть созданы – user463035818

+3

Я не убежден в вашей мотивации. Полиморфизм виртуальных функций предназначен для обработки различных реализаций * во время выполнения *. Вариант использования нескольких платформ, по-видимому, не имеет решений во время выполнения. Ваша программа не выполняет * в то же время * на игровом автомате и на игровой станции. Скорее, вы перекомпилируете для каждой платформы. –

+0

На самом деле я не понимаю проблему, если вы хотите, чтобы объект, который был получен из абстрактного класса в качестве члена другого класса, вы можете иметь указатель на этот абстрактный класс как член – user463035818

ответ

1

Чтобы иметь различные реализации на различных платформах, вы не должны использовать полиморфизм, вы скорее должны использовать старый добрый #if __linux__, #if определяется __mac__ & & определенной __darwin__, #if _WIN32 и т.д.

Если вам нужно чтобы получить максимальную отдачу от реального процессора, в конечном итоге программа запускается, и вы хотите, чтобы реализаций были специально оптимизированы для каждого возможного CPU, тогда это совсем другая история, потому что для выбора наилучшей реализации вам нужен какой-то механизм времени исполнения.

0

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

Если это то, что предназначено, то используйте абстрактный класс. Если базовый класс должен быть использоваться как есть, то обеспечить пустой рутину

class BASE { 
public: 
    void SomethingUnusedHere() {} 
}; 

Пользователь может сделать то же самое, если он действительно не имеет значения, что процедура делает и до сих пор не требуется в использовании класса.

class BASE { 
    : 
    void Func() = 0; 
    : 

} 

class CHILD : public BASE { 
    : 
    void Func() {} 
    : 
} 

Конечно, если Func действительно необходим, то он должен содержать правильный код;

+0

Проблема в том, что у меня есть только объявление базового класса, но должен использоваться дочерний класс. Если вы используете ваш пример, конструктор 'CHILD' должен быть вызван где-то, что невозможно, если использовать' BASE' в качестве переменной-члена. –

+0

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