2014-02-02 5 views
0

Я довольно новичок в C++, и мне нужно прояснить перенос проекта с Java.Полиморфный подтипирование в C++

В 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. В производных классах I» m разрешено использовать методы и переменные указанного производного класса (например, serverName/clientName в CentralNetwork/PeripheralNetwork), а не только базового класса

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

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

  4. Каждый метод/переменная, использующая общие элементы, будет обрабатываться в производном классе как дочерний класс, а не базовый класс (например, в 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++ (с шаблонами)? – dyp

+0

Пожалуйста, добавьте все строки в блок кода с четырьмя пробелами или выберите его и нажмите Ctrl + K, чтобы получить правильное форматирование. http://stackoverflow.com/help/formatting – dyp

+0

@dyp done, извините, мой первый пост ... – SilverHawk

ответ

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

спасибо парковски, это решает третий пункт, но мне также нужны другие. – SilverHawk

+0

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

0

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

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

(*) Там будет шаблон ограничений, мы надеюсь, в C++ 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

Я должен попробовать, по всем пунктам, я дам вам знать. Пока, _Thanks_. – SilverHawk

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

Есть ли причина, потому что вы использовали структуры вместо классов? – SilverHawk

+0

@SilverHawk Когда 'public' по умолчанию для structs делает лучшую работу, описывая общую доступность внутренних компонентов, я использую ее. – jthill

+0

@SilverHawk мне пришло в голову одно: если я правильно понимаю, переменные класса в Java всегда являются ссылочными типами, даже теми, которые уже являются членами класса, такими как ваш 'masterHost', и если так вы ожидаете своего' getMasterHost() ' для возврата ссылки на фактический объект masterhost, который он будет, в Java, и не будет, на C++. Вместо этого он вернет отдельную копию встроенного объекта, выделенного в стеке, если где-нибудь и будет уничтожен, когда он выходит за рамки. – jthill

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