Полиморфный подтип в C ++

0

Я новичок в C++, и мне нужно уточнить перенос проекта с Java.

В Java я могу объявить базовый класс и его производные с помощью дженериков следующим образом:

public class GenericHost{
    public enum HostType{
        server,
        client
    }
    public HostType type_;
}

public class MyClient extends GenericHost{
    public String clientName;
}

public class MyServer extends GenericHost{
    public String serverName;
}



public abstract class GenericNetwork<hostType extends GenericHost> {
    public enum NetworkType{
        central,
        peripheral
    }
    private NetworkType type_;
    protected hostType masterHost;
    public hostType getMasterHost(){
        return masterHost;
    }
    public abstract String getName();
}

public class CentralNetwork extends GenericNetwork<MyServer>{
    @Override
    public String getName(){
        return masterHost.serverName;
    }
}

public class PeripheralNetwork extends GenericNetwork<MyClient>{
    @Override
    public String getName(){
        return masterHost.clientName;
    }
}

Это позволяет мне:

  1. В производных классах мне разрешено использовать методы и переменные указанного производного класса (например, serverName/clientName в сети CentralNetwork/PeripheralNetwork), а не только базового класса

  2. Производный класс задан, поэтому компилятор/редактор может предложить мне каждый метод и переменную во время редактирования кода

  3. Я вынужден использовать класс, который получен из базового класса (GenericNetwork/GenericHost), каждая ошибка во время компиляции и не запускается

  4. Каждый метод/переменная, использующая generics, будет обрабатываться в производном классе как дочерний класс, а не базовый класс (например, в CentralNetwork, getMasterHost вернет полученный MyServer, а не базовый GenericHost).

Я хочу знать, существует ли что-либо подобное в C++. Я уже искал шаблоны, наследование и подтипирование, но я не могу найти способ сделать что-то умнее, чем в Java. Надеюсь, я что-то пропустил...

EDIT: Это то, что я пытался в C++:

class GenericHost{
    public enum HostType{
        server,
        client
    }
    public HostType type_;
}

class MyClient : public GenericHost{
    public String clientName;
}

class MyServer : public GenericHost{
    public String serverName;
}

template<class hostType : GenericHost>             <--WISH, forced base class
class GenericNetwork {
    public enum NetworkType{
        central,
        peripheral
    }
    private NetworkType type_;
    protected hostType masterHost;
    public hostType getMasterHost(){
        return masterHost;             <--WISH, should return MyServer / Myclient in derived class
    }
    public virtual std::string getName();
}

class CentralNetwork<MyServer> : public GenericNetwork{
    public std::string getName(){
        return masterHost.serverName;             <--WISH, tiped and suggested by editor / compiler
    }
}

class PeripheralNetwork<MyClient>: public GenericNetwork{
    public std::string getName(){
        return masterHost.clientName;             <--WISH, tiped and suggested by editor / compiler
    }
}

У меня сейчас нет проекта C, поэтому я переписал его на лету, извините за любую ошибку...

  • 1
    Можете ли вы показать нам, что вы пробовали в C ++ (с помощью шаблонов)?
  • 0
    Пожалуйста, добавьте все строки в блоке кода с четырьмя пробелами или выделите его и нажмите Ctrl + K, чтобы получить правильное форматирование. stackoverflow.com/help/formatting
Показать ещё 4 комментария
Теги:
polymorphism
inheritance
subtyping

3 ответа

0

Как отмечают все, шаблоны C++ могут реализовать это, поэтому он не заслуживает выделенного синтаксиса.

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

#include <string>
struct GenericHost {
        enum HostType { server,client } type_;
};

template<class GenericHost=GenericHost>
struct MyClient : GenericHost     {  std::string clientName;  };    

template<class GenericHost=GenericHost>
struct MyServer : GenericHost     {  std::string serverName;  };


template< template<class> class SpecificHost, class GenericHost=GenericHost >
struct GenericNetwork
{
        typedef SpecificHost<GenericHost> hostType;
        virtual ~GenericNetwork() { };

        enum NetworkType { central, peripheral };

        hostType             getMasterHost() { return masterHost; }
        virtual std::string  getName() = 0;


protected: hostType     masterHost;
private:   NetworkType  type_;
};


struct CentralNetwork : GenericNetwork<MyServer> {
        std::string getName() { return masterHost.serverName; }
};

struct PeripheralNetwork : GenericNetwork<MyClient> {
        std::string getName() { return masterHost.clientName; }
};


// testing: force instantiation:
struct CentralNetwork makeme;
struct PeripheralNetwork metoo;
std::string doit() { return makeme.getName() + metoo.getName(); }

Я считаю, что это получает все четыре желания, хотя ошибки будут обнаружены в разных местах. Как и другие, укажите static_cast<requiredBase*>((testedClass*)0); может выполнить эту работу, но в обход защиты требуется работа, а не просто ошибка, и она появится в системе типов, поэтому я не вижу смысла.

(отредактируйте: добавьте виртуальный деструктор, не десерт для меня сегодня. Плохая собака.)

  • 0
    Есть ли какая-то причина, потому что вы использовали структуры вместо классов?
  • 0
    @SilverHawk Когда public стандарт для структур лучше описывает общую доступность внутренностей, я использую его.
Показать ещё 3 комментария
0

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

template <class hostType>
class GenericNetwork {
public:
    GenericNetwork() {
        static_cast<GenericHost*>((hostType*)nullptr); // or 0 or NULL if not C++11
    }
};

Если hostType и GenericHost совместимы, бросок будет успешным, но ничего не сделает. В противном случае вы получите ошибку времени компиляции.

  • 0
    спасибо parkovski это решает 3-й пункт, но мне также нужны другие.
  • 0
    @SilverHawk Насколько я вижу, этот (и мой) подход решает все, кроме 2 (и, может быть, даже это, я не совсем уверен, каков контекст). С шаблонами вы не теряете статическую безопасность типов в C ++.
0

Нет специальной функции для явного ограничения аргументов шаблона (*), но вы можете использовать (С++ 11)

static_assert( std::is_base_of<GenericHost, hostType>::value,
               "The template argument must be derived from GenericHost" );

(*) На С++ 17 будут ограничены шаблоны.

Это утверждение времени компиляции и может использоваться как объявление:

template<class hostType>
class GenericNetwork {
    static_assert( std::is_base_of<GenericHost, hostType>::value,
                   "The template argument must be derived from GenericHost" );

public:
    enum NetworkType{
        central,
        peripheral
    }

    virtual ~GenericNetwork(); // you typically want a virtual dtor in an ABC

    hostType& getMasterHost(){ // you might want to return a (const) reference
        return masterHost;
    }

    virtual std::string getName() = 0; // abstract

private:
    NetworkType type_;
    hostType masterHost;  // or protected

    // consider making the copy ctor and copy assignment op protected
    // to prevent unintended slicing
}
  • 0
    Я должен попробовать, по всем пунктам, я дам вам знать. Сейчас спасибо .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню