Я довольно новичок в 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;
}
}
Это позволяет мне:
В производных классах мне разрешено использовать методы и переменные указанного производного класса (например, serverName
/ clientName
в CentralNetwork
/ PeripheralNetwork
) и не только базового класса
Производный класс имеет чаевые, поэтому компилятор / редактор может предложить мне каждый метод & переменная во время редактирования кода
Я вынужден использовать класс, производный от базового класса (GenericNetwork
/ GenericHost
), каждая ошибка происходит во время компиляции, а не во время выполнения
Каждый метод / переменная, которые используют обобщенные значения, будут обрабатываться в производном классе как дочерний класс, а не как базовый класс (например, в CentralNetwork
, getMasterHost
вернет производное MyServer
а не база GenericHost
).
Я хотел бы знать, существует ли что-нибудь подобное в C ++.
Я уже искал шаблоны, наследование и подтипы, но я не могу найти способ сделать что-то умнее, как я делал в Java. Я надеюсь, что что-то пропустил …
РЕДАКТИРОВАТЬ:
Это то, что я пробовал в 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, поэтому я переписал его на лету, извините за любую ошибку …
Насколько я знаю, нет явной функции, которая позволяет вам сделать это. Ты можешь использовать static_cast
однако, это даст вам ошибку времени компиляции, если типы несовместимы.
template <class hostType>
class GenericNetwork {
public:
GenericNetwork() {
static_cast<GenericHost*>((hostType*)nullptr); // or 0 or NULL if not C++11
}
};
Если hostType
а также GenericHost
совместимы, актерский состав будет успешным, но ничего не делать В противном случае вы получите ошибку времени компиляции.
Нет специальной функции для явного ограничения аргументов шаблона (*), но вы можете использовать (C ++ 11’s)
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
}
Как все отмечают, шаблоны 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(); }
Я считаю, что это получает все четыре desiderata, хотя ошибки будут обнаружены в разных местах. Как указывают другие static_cast<requiredBase*>((testedClass*)0);
может выполнить эту работу, но обход защиты требует работы, а не просто ошибки, и она будет отображаться в системе типов, поэтому я не вижу смысла.
(редактировать: добавить виртуальный деструктор, сегодня вечером для меня нет десерта. Плохая собака.)